Waking Up Neglected Scooter Battery Pack

One of my long standing to-do list items is to understand and master control of brushless DC motors. My goal is small robots, so I want to work with motors bigger than tiny multirotor aircraft motors and smaller than beefy electric car motors. Towards the upper range of my interest (before their voltage/amperage start to get scary) are motors used in two-wheeled balance scooters. As a Back to the Future fan, I am offended these devices have been marketed under the name “hoverboard” but that is out of my control.

I’ve been waiting for their prices to drop and they seemed to have settled near the $100 mark. When my local Target advertised a sale dropping a “Jetson Rogue” below $100, I got one with the intent of taking it apart to see what’s inside. But I have yet to do so and it has since gathered a thick layer of dust. It occurred to me I should charge up the battery occasionally. I plugged in the charger and… nothing. Hmm. Not good.

Opening it up, I found its battery pack had a single commodity XT60 connector and nothing else. I can work with that.

I’m mildly concerned by the fact this device had wires that didn’t go anywhere. This white wire has a crimped metal end but it’s not part of any connector I could find.

This properly crimped connector is likewise missing anything to plug into.

Those are mysteries to be solved later. Right now I want to see if the battery is dead or if it can be revived. I unplugged the XT60 connector and measured approximately 3V across its terminals. This is low even for a single cell. The label said it was a 7S2P battery pack with a nominal voltage of 25.9V so yeah, I had waited too long to keep it properly charged. Over-discharging is bad, but it is not necessarily fatal.

I set my bench power supply to deliver up to the full 29.4V appropriate for seven lithium-ion cells in series, but limited to a current of 50 mA. This is 5% of the rate delivered by its charging power adapter and should be very gentle on the battery cells in their delicate over-discharged state. I connected the battery, and my power supply control panel showed power delivery was jumping around. 8V for a second, then no power draw at all for several seconds. Then 9V for a second, and things cut out again. This was unexpected. I disconnected my power supply to investigate further.

Thankfully this battery pack enclosure was held together with screws, so I could open it up for a look inside. Aha! It is not a raw package of 14 cells in 7S2P configuration, they are under care of a battery management system (BMS) board. Similar to the 3S BMS board I had played with, but this one handles 7S cells. There are spot-welded tabs to monitor voltage of each 2P group. Some of those spot welds are… not textbook, let’s say. But they have continuity and they let me measure voltage. The situation looks even better here. Each 2P group (B- to B1, B1 to B2, etc. through B6 to B+) ranged from 1.1V to 1.9V. The pack as a whole is actually hovering around 10V across its “B+” and “B-” terminals. This is still over-discharged but in far better shape than 3V I measured across output terminals. (P+ and P-)

While the BMS chip is designed to allow charging across P+ and P- terminals, a over-discharged battery is outside of its normal operating range. For reasons I don’t understand, this chip is cutting in and out as I tried to charge with gentle low amperage. The chip may be taking deliberate action to work with over-discharged cells, but it may be getting confused by either the low voltage or my low amperage. There’s a chance the chip’s algorithm is confused and is on a path to destroy itself, the battery cells, or both.

I decided not to trust the chip and go with what I know: steady charge at a gentle low rate. With the pack open, I could connect my bench power supply directly to B+ and B- terminals. This bypassed the BMS chip and allowed me to slowly bring up voltage across the whole pack. Many hours later, pack voltage was up to its nominal 25.9V and I disconnected my power supply. I left the pack sitting overnight so internal chemistries can take its time balancing everything out. It was left outdoors in a bucket, just in case the chemistry decided to get angry. The next day I was happy to see it did not burst into flames. I put it back into the scooter. Now the scooter was willing to power up, and was also willing to take power from its charging adapter. I think we are back in business.

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.]

Harbor Freight Mini LED Flashlight Teardown

I want to learn CadQuery to see if I can use it for my 3D printing projects, but at the moment I’m drawing a blank for a good project to climb the learning curve with. I looked over my teardown queue for potential inspiration and decided to take apart a Harbor Freight mini LED flashlight. This was one of the items they used to give away “Free with Purchase” to entice people like me to stop in, so it must have been made cheaply even by Harbor Freight standards. A good item to compare & contrast with an earlier giveaway keychain LED teardown.

The push button switch at the end unscrews to release a battery tray holding three AAA batteries. Given Harbor Freight price points, it’s not a surprise these batteries inevitably leak and destroy the device. I have several of these little flashlights and I picked this one as it had yet to corrode. The trio of batteries were wired in series, for a theoretical maximum of 3 * 1.5V = 4.5V.

I found no fasteners on the head of the device, nor any signs of glue, so it might be held together by friction alone. I pulled out my pliers and the thin metal construction could be peeled apart. It seems to be roughly the thickness of a food can, but peels much more easily, so probably aluminum instead of steel.

After unrolling that lip, I could remove the clear plastic cover, the shiny plastic reflector, and a circuit board with nine 5mm white LEDs soldered in parallel. There were no signs of a current-limiting resistor, so this design must have been dependent on cheap alkaline AAA battery internal resistance to keep this thing from burning itself up.

Connected to my bench power supply, I gradually dialed up the voltage delivered to this array of nine LEDs. At just under 3.3V, the power supply reported supplying 9 * 20mA = 180mA of current. I guess three cheap alkaline AAAs asked to supply 180mA would sag to 3.3V or less. Either that or the designer of this cost-optimized design decided they don’t care if these LEDs burn out.

LIN Bus The Little Sibling Of CAN Bus

Today I learned about existence of Local Interconnect Network or LIN bus. It is a set of specifications defining a network communication system describing everything from raw voltage levels up to logical communication data protocol. A LIN network has up to sixteen devices: one controller and up to fifteen peripherals, and they communicate via asynchronous serial over a shared wire. At a high level, there’s a lot of resemblance to how serial bus servos work, but LIN came from the automotive field.

As car components got more sophisticated, basic on/off or analog voltage was no longer enough to convey status. They needed an actual data network protocol, and the industry has settled on CAN bus. While capable and robust, CAN bus is overkill for basic functions. LIN bus is a simpler protocol that trades capability for less expensive implementation hardware. A focus on simplicity meant the LIN steering group decided their job here was done and is now a mature standard also known as ISO 17987.

Based on my limited reading, I understand the division of labor for an illustrative modern car driver’s door would be something like this:

  • A host controller connected to the car’s CAN bus communicating with the rest of the car, and connected to other devices inside the door via LIN bus.
  • A microcontroller monitors all the power window switches and report their status to the door host via LIN.
  • A microcontroller manages the power window motor based on commands received from door host via LIN.
  • Repeat for power door locks, and power mirrors.
  • Other sensor inputs like whether door latch is closed.
  • Other control outputs like illuminating puddle lights.

Given the close proximity of these devices to each other inside the door, full signal integrity robustness of CAN bus was not strictly necessary. LIN bus modularity means fewer wires. For example, wires that would otherwise be necessary if the host controller had to monitor each power window switch individually could now be replaced by a single LIN data wire. It also simplifies adding/removing functionality for higher/lower trim levels in a product line, and reduce cost of adapting to regional differences like left- or right-hand drive.

That’s all great for automobile manufacturers. But since LIN bus is simpler than CAN bus, it also lowers the barrier to repurposing automotive components for hobbyist projects. A quick look on Arduino forums found several threads discussion LIN bus devices, and there exists affordable breakout boards (*) to interface with microcontrollers. This could be an entryway into a lot of fun.


(*) Disclosure: As an Amazon Associate I earn from qualifying purchases.

Window Shopping Hugging Face LeRobot

Recently there have been a lot of excitement around several fields of machine learning that broke out of their research paper phase. Meaning they got good enough for everyday people to see what they could do without having to chew through dense research lingo. Most of the hype centered around large language models powering ChatGPT and friends. Generative systems have also come of age. I played around with image generators briefly, but there are also audio and video generators out there.

None of those systems are specifically applicable to a rover brain, so while technically interesting I didn’t see anything I wanted to pursue yet. I knew it was a matter of time, though, for those technologies to pioneer enough infrastructure leading to something that would produce rover-applicable machine learning systems. This might have just happened, with the launch of LeRobot by Hugging Face.

According to Wikipedia, Hugging Face company has a few related divisions. One of them is Hugging Face Hub, a repository of machine learning models and datasets. Sort of like how GitHub is a repository of source code, but at a higher conceptual level and focused on machine learning. The index of models on Hugging Face Hub are tagged by topic. If I click on any of the “Natural Language Processing” tags today, they return tens of thousands of models. But if I click on “Robotics”, there are only 27 models. LeRobot is an effort by Hugging Face to jumpstart this field and I’ll be watching to see if it takes off or fizzles out.

Rerun.io

Regardless of LeRobot’s success or failure, a quick skim through its documentation taught me a lot and I didn’t even follow all its links to other projects. Out of those I looked at, the most promising gem is data visualization tool Rerun.io. Skimming through its documentation and examples, Rerun sounded like something right out of my dreams: a time-based data visualization tool not just on 2D graphs like Grafana, but also visualize data in 3D space. From simple spinning LIDAR to depth cameras, Rerun claims to put all data into a single time-indexed visualization. I will definitely take a closer look in the near future.

Mama And Baby Bird

A tree in my yard has grown tall enough to approach some overhead wires, so I started trimming it back. A few weeks ago I climbed my ladder and a bird I hadn’t noticed took off. The takeoff point was less than a meter from my face so it was startling. I thought “oh, a bird, OK” and went about my business. It didn’t occur to me to think about the fact local birds usually keep much greater distance.

The next day I climbed the ladder to continue trimming, and was startled again by a nearby bird. Once may have been a random bird that happened to perch on a tree, twice in the same place is not a coincidence. I looked closer and finally noticed a nest with an egg within. Oh no, I had interrupted a very important project!

Mama bird had stayed with her well-camouflaged nest as long as she could, but when I got within arm’s reach it was too much for her. That’s why she took off so close to my face. Sadly, my trimming meant her nest wasn’t as well camouflaged as it used to be. I couldn’t exactly glue those branches back on, but I could pause my trimming until she finishes her project.

I’ve read that some prospective mother birds would abandon a nest if it proved too dangerous, and I was worried it might happen here. Next day I was happy to see she had returned, now that I know where to look and know to keep my distance. I pulled out my big zoom lens and took this picture from 5-6 meters away.

Over the following weeks I checked in on mama bird daily. She was not happy with my tree trimming and erected additional camouflage to mitigate my damage. That made it more difficult to see her but hopefully provided some protection against predators. I hadn’t trimmed the tallest branches yet so she still had decent overhead coverage against other birds, but I was worried about cats in the neighborhood who could now better see the nest from ground level.

Due to mama bird’s new additions and the fact I kept my distance, it was hard to know if her project was progressing. Her perch position seem to change as time went on, but I couldn’t see anything definitive. Besides, if that egg had hatched, it would make sense for her to sit shielding her baby from the exposed side of the nest.

But eventually I saw baby bird sitting on the “dangerous” side of the house, prompting me to pull out my camera equipment again for this family photo. Less than a week after this picture, my daily check of the nest found it empty. It remained empty every day for a week. I hope the family voluntarily moved on to a safer home, but I will never know for sure. Either way, they’re gone now so I resumed trimming my tree.


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.

My Desired Hyundai Doesn’t Exist (Yet?)

I had contemplated buying a Hyundai IONIQ 5, but poor dealership experience turned me off of that path. Which was just as well, because my ideal Hyundai isn’t available for purchase anyway. An IONIQ 5’s SUV form factor is not my first choice as it is far larger than I want in a car. What I would love is to have that design language in a 2-door sports car.

We got a preview of that idea’s greatness with Hyundai’s N Vision 74 concept. It has a hydrogen-electric hybrid powertrain featuring fuel cells and batteries feeding electric motors. Optimized for high performance on the race track, complete with aerodynamic assists like a deep front splitter and a huge rear wing. But most importantly, it applied IONIQ 5 school of design to a sports car form factor. This has been very well received with many calling for Hyundai to put the concept into production. I’ve read that Hyundai is contemplating building a very limited number of them as six figure track toys for the rich, which isn’t actually what I had hoped for when I wished for a production version.

I don’t want the N Vision 74 as-is, I wanted a production car that the concept race car implied. Something without race aerodynamics and with an interior usable for daily living. Remove the hydrogen tank and fuel cell leaving a pure battery electric car. And since I’m wishing anyway, sell it around IONIQ 5 price range and make it smaller. According to numbers floating around on the internet N Vision 74 is about twenty inches longer and eight inches wider than my 2004 Mazda RX-8. That’s enormous! Some of that length might be aerodynamic bits and some of that width are race car tires, but it clearly casts a big shadow.

For an analogy, Hyundai recently released a race-ready variant of the IONIQ 5 N called the eN1 Cup car. Featuring suspension, tire, and aerodynamic aids appropriate to track duty. The relationship between that eN1 Cup car and a normal IONIQ 5 is the same relationship between the N Vision 74 and an electric two-door coupe of my dreams. Too bad that car does not exist but, if it ever comes into being, it might entice me to walk back into a Hyundai showroom.


Images from Hyundai news room

Plug-In Car Research Eliminated Hyundai

My time with my 2004 Mazda RX-8 may be coming to an end given fading interest in doing my own work and condition of its engine. For now it has fresh coolant as part of recent radiator replacement. I also replaced the engine air filter and gave it an oil change. Brakes and tires both have plenty of life left, so that takes care of all minor wear and tear items. It is all set to ride until the next major event which will demand a big decision. To help inform that decision, I visited a nearby cluster of car dealerships. Going west to east on Gale Ave. in Puente Hills, CA, for an in-person look at various plug-in vehicles.

Toyota Prius Prime

First stop was Puente Hills Toyota to look at their new plug-in hybrid. I had leased a Chevrolet Volt from 2012-2015 and thought GM had the start of something good. Give it three generations of evolution and it should be a really compelling product. Sadly GM killed the Volt after just two generations, but Toyota continued developing their counterpart and a new Prius Prime was recently released. It is a hit! So much that Puente Hills Toyota allocation are sold before they even reach the lot. I could look at an already-sold Prius on the lot but none were available for a test drive. Toyota will make plenty, I’m sure, I just need to wait a bit for my hands-on time.

Chevrolet Bolt

A block away was Chevrolet of Puente Hills, with a red 2023 Chevrolet Bolt on the lot. Bolt production situation is opposite of the new Prius: Chevrolet shut down Bolt production late 2023 so dwindling inventory will not be replenished. I explained my situation to the employee who greeted me and asked for to take the Bolt around the block. The short drive was uneventful and mostly quiet, since I explained up front there will be no deal today.

I think the Bolt is a sufficient and capable compact car. It had much of the GM traits that I remember from my Volt, and it checks all the boxes for basic electric transportation. As a battery-only car it doesn’t have the road trip gas-and-go capability of a Prius Prime, but I rarely take such trips so it’s a tradeoff I’d consider making.

Nissan Leaf

Puente Hills Nissan was next door but I did not stop in. Leaf has toned down visual weirdness of earlier generations but still not a sight I’d enjoy walking up to. Nissan also decided against a liquid-cooled battery pack on cost and complexity grounds, an engineering decision that has been blamed for Leaf battery degradation faster than other EVs on the market. And finally, Nissan had bet on the wrong horse for high-speed charging. CHAdeMO lags far behind other alternatives in North America.

I took a brief look at Ariya, which addressed the battery cooling and charge plug challenges of a Leaf but it came across as a very generic vehicle with nothing that grabbed my attention as worth a price premium over a Bolt.

Ford Mustang Mach-E

The Mach-E, in contrast, was more distinctive. I studied its styling for a few minutes on the lot of Puente Hills Ford next door. I’m no Ford Mustang purist so Mach-E’s mere existence did not offend me, but I decided its Mustang-inspired styling didn’t attract me either. Still, I had little to lose by taking it for a spin around the block. Despite my clear up-front statement I would not be buying today, the representative made an earnest pitch. I appreciate his effort to make a case for my business. I found the Mach-E distinctive, but not in any way that enticed me to pay more over a Bolt.

Hyundai IONIQ 5

But the Hyundai IONIQ 5, now we’re talking. It has a creased-edge retro-future style I find much more attractive than a Bolt. Though they’re not really head-to-head competitors. The fact they both look like 5-door hatchbacks in pictures is a deception of proportions: Ioniq 5 wear 21″ wheels in advertisements, the Bolt rolls on 17″ wheels. So the car is bigger than I want, and those big tires promise big replacement bills to come, but I was willing pay a price premium for that styling.

I walked in to Puente Hills Hyundai and explained that I was researching potential cars and asked to take an IONIQ 5 around block. I was denied and told to come back when I’m serious about buying. This was quite jarring immediately following my experience at Ford! So I didn’t get any data on how well I fit in the car, but I did get data on how Hyundai treat customers. That’s enough to eliminate IONIQ 5 from consideration.

Summary

Toyota Prius Prime is a sleek sedan that can drive mostly electric but can run on gas for road trips, built with Toyota quality for the long haul. Production is still ramping up.

Chevy Bolt has an overstyled busy exterior and economy class interior, but it should be a practical and economical box until/unless infamous GM quality rears its head. Inventory is running out.

Leaf, Ariya, and Mach-E failed to build a compelling case over a Bolt and/or Prius Prime.

Hyundai IONIQ 5 styling looks fantastic, but given my dealership experience I will spend my money elsewhere.

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.