lfcode.ca notes compiled for future reference

I competed in Skills Canada Robotics

Skills Canada hosts a robotics competition for the secondary level every year with a different task each time. Competitors build remote controlled robots ahead of time which they bring with them to the competition. There is also an autonomous portion of the competition where we build robots on the competition floor using a set of parts for a challenge which is revealed on competition day.

Our team achieved first place at the national competition, but we are not continuing to worlds as it is not a worlds qualifying year (though a former team from my school is!).

image of the court

This is the court we played on with some other teams on it. It has hills on both sides (themed after the Citadel in Halifax), with ammo boxes full of foam golf balls on top of the hills and on the court floor. The objective is to pick up and deliver these foam golf balls to the other side using a maximum of 2 remote operated robots (autonomous robots can be used in addition to these two but few teams chose to do this).

Scoring is as follows:

  • 1 point for each ball that is delivered onto the court floor of the other side of the court
  • 2 points for each ball that is in the nets on the hills at the end of the game
  • 3 points for each ball in the nets on the opposing team's robots at the end of the game
  • 10 points if all robots with nets end the game on top of the hill as the buzzer sounds

We built two identical robots for the competition, where we 3D printed almost all of the mechanical parts. The robot design we built uses hacked car vacuums to suck up the foam golf balls into a tube where they are buffered. A rotating valve similar to a ball valve is used to allow balls to flow into the launcher and to block off suction to the launcher while collecting balls.

To launch the balls, we use a mechanism similar to a pitching machine which launches balls with two spinning wheels. Balls are pushed into the pitching machine with a server fan.

diagram of robot internals

On the front of the robots, we built a height adjustment mechanism using a 270-degree servo and a rack and pinion from a Tetrix Max kit we got from the last time we went to Nationals.

Technical Details

  • there is one 12V 5000mAh lithium polymer battery powering everything
  • motor controllers are: Vantec RDFR22 and Sabertooth 2x25, the Vantec for our 4 motor drive system and the Sabertooth for the vacuum and the height adjustment linear actuator. The launcher is handled with a RC relay board which provides on/off control for the fan and motors
  • RC system: we use Jumper T8SG-v2 Plus radios on the FlySky protocol; there is a 10 channel receiver installed, and we use 8 of those channels for controlling the robot. I've used a DigiSpark clone board to extend the servo input range of our nozzle height adjustment servo to get full travel as well as output a PWM signal to slow down the server fan (see https://github.com/lf-/ServoExtender).

Evaluation of techniques used

We made extensive use of various fusion welding techniques on the plastics in this year’s design to varying degrees of success. The launcher was heat staked on very successfully. Friction welding was used on the assembly of the vacuum and the tube on it as well as attaching that vacuum and tube assembly to the launcher, also successfully.

We superglued on the fan bracket, which later failed on both robots, one at competition and one in practice. The first one fell off because the robot fell on its back (oops), so I friction welded it back on. The robot fell over again in practice (oops) and broke again so I heat staked it back on (friction welding is hard to redo over an existing weld). The other one fell off at competition, and I don’t think it was because the robot fell over. As it was a glue failure, I friction welded it back on and it was fine for the rest of the day.

Hot glue was used to attach the aiming device, which was generally successful, though airline shipping damage caused us to need to reattach one of them at orientation. Hot glue and velcro have both been used to attach electronics, and I am not satisfied with the results of either on the aluminum buck converters. Further research is required, possibly involving 3d printed backing plates.

Autonomous competition

The Skills competition had a segment where competitors were to build robots that drive themselves through a maze and drop off plastic spools in a couple of positions on the court.

autonomous-robot

We built the simplest and smallest possible frame we could think of, using staggered motors to make it narrower.

The spools were managed by two servos with arms holding pins inside the spools. When the spools are to be dropped, the servo with the pin is simply lifted and the spool deposited.

Our team was the only team to build a significant piece of software ahead of the competition (which you are allowed to do), specifically, I wrote a system that allows for the autonomous robot to be manually driven into each desired position, and the motor encoder counts measured. These counts are dumped to the serial port, and they can then simply be pasted into the program to drive automatically. This turned a 2 day programming task into a 2 hour driving task.

More photos of the robots and of the internal mechanisms, with an emphasis on the 3D printing are available from https://imgur.com/a/de6Y6zK.

Tags: 3dprinting, electronics, school

Nspirations on getting math done faster

I enjoy math so much that my primary goal is to get it done as quickly as possible. In more practical terms, the better I can get stuff done on my Nspire, potentially the higher score I can get on the AP exams.

The Nspire is not undocumented, just that the documentation is very well hidden. It's also not sorted by how often you might use something.

Ctrl shortcuts

The fastest way to enter stuff is either by memorizing the menu numbers (you can press the number key which shows up on a menu to go straight to it), though that often puts you in a dialog box, or by typing it in. Unfortunately, typing stuff in is not always easy and there are many characters which seem to have no way to be typed other than by selecting them from the library or in the character list.

The most significant ones are the \ (shift-divide) and the _ (ctrl-space). The backslash is useful for libraries, for example: ch\mm, and the underscore is useful for annotating units, but I use it mostly for getting the constants such as _Rc (8.31 J/g·°C) and _nA (Avogadro's number).

Many of the usual shortcuts as you might use on a computer are also available on the Nspire, for instance, Ctrl-C, Ctrl-V, Ctrl-X, Ctrl-A (specifically with this one, I like to enter square roots as typing the inside, Ctrl-A, then the square root button). Selection can be done by shift-arrows or with the cursor as follows (note: works on computers too, awesome for copying an entire page): click the mouse where you want to start a selection, then shift click where you want the end.

For Calculus, some of the most important shortcuts are Shift-plus and Shift-minus, which are the integral and the derivative. One way to remember these is to think of what evaluating the given thing would do to the exponents in a polynomial. Integrals increase these exponents, and derivatives decrease them.

If there's anything you should take away from this post though, it's the cursor navigation shortcuts! They are in the same arrangement as you would see on a computer numpad. That is to say, Ctrl-1 is home, Ctrl-7 is end, Ctrl-9 is Page Up and Ctrl-3 is Page Down.

Graph environment

The most interesting thing about the graph environment is what I call the right click, which brings up the context menu for whatever is under the cursor (Ctrl-Menu). From this, you can access recent commands and other stuff:

Annotation-2019-04-16-172723

To do stuff precisely, for example when you are finding an integral between 0 and 2, you select the integral command, then type 0 on the keyboard, press Enter, then press 2, then Enter.

To get the precise coordinate of some point, for example an intersection, click once on the text of the coordinate you want to store, press Ctrl-Var (sto->), and it will give something like var := 123.45. Enter the variable name you want, and press enter. You can then access the information about that variable in the right click menu of the text.

If that point doesn't yet have coordinates displayed, for instance if you placed it from the geometry environment and you need to move it to some precise position, you can give it some by clicking on the point, then using the right click menu and selecting "Coordinates and Equations".

Tags: nspire, school, software

Software that respects users' privacy must inform them if they are going to compromise it

fusionleak

Above is a STEP file from Autodesk Fusion 360. It contains personally identifiable information by default: it leaks their Autodesk username (in my case, my full name!) and a file path on the local computer, which could also contain the user's name as well as any other information they might have put in it. In this case, it identifies where a non-scrubbed version of this particular file is found.

Fusion 360 does not tell you that this information is there. It does not display it in the interface either.

This sort of metadata leaking is everywhere. For instance, I have no idea if I can get an email associated with the owner of a Google document if it is shared with me. It's not obvious if it is exposed in the UI, and if it is not, perhaps an API exposes it. This sort of issue is particularly insidious because it makes it easier to use a platform to conduct doxing attacks and makes it unclear whether people whose identities need to remain private can use a service.

Metadata is more interesting than the data itself. This is a central concept in the NSA's phone surveillance: the content of a call can be surmised particularly easily by a computer simply by considering origin, destination and duration.

The primary data in a file is usually completely generated by the user and is very unlikely to contain any PII unless they put it there themselves. Metadata on the other hand is frequently computer generated, is hard to read relative to the data itself, usually hiding in dialogs in dusty corners of the user interface, if exposed at all, and is likely to contain information about the user and their computer.

If you are writing a program which generates files or other information which will be shared, please consider what you store as metadata with it. Do not store local paths on the user's computer in the file because they may compromise the user's privacy. Show the user what metadata is on the file when they are saving it. Everywhere in the interface where taking some action may reveal information as metadata to someone else, include a small block of text indicating what information that is and why it needs to be collected. Similarly to how rubber duck debugging works, you may notice while you're writing that statement that you don't need to expose some of the information. As much as Apple is a harmful company to the environment and to users' ownership of their devices, I have to commend them on their choice to include a small privacy icon wherever the user is agreeing to provide some information in the provision of a service.

These metadata issues are something which really made me realize how fortunate and privileged I am to be in a situation where having my name published with CAD files is at best annoying. I can think of several people I know online for whom that would be catastrophic, and they are all from groups which have been and continue to be prejudiced against in society. If a team has people from those groups on it, it is far more likely to notice this type of privacy issue and prioritize it appropriately highly.

Tags: software, software-politics

I designed and built a mechanical macropad/numpad!

macropad-in-hand-small-1

More images are available at the imgur gallery documenting this project.

I built a macropad based on an Arduino Leonardo 2 years ago to rectify my Unicomp Model M keyboard lacking media buttons (volume, media, and others). Around June 2018, I further developed that macropad by adding a 3D printed case for it:

old mediapad

It served me well, but it was always frustrating to have keys not always register when pressed, and I wanted to get a Tenkeyless keyboard in order to get more mouse space and place my keyboard more ergonomically.

The obvious solution was to get some sort of mechanical numpad, but my limited research those made it abundantly clear that not only were these difficult to get ahold of in Canada, I probably could not get media buttons with them, somewhat defeating the purpose of getting one. Plus, I wanted an excuse to do some electronics.

I came up with the following design requirements:

  • Must have a layer indication on the front
  • Should have a numpad layout in case I want to use it as one
  • Must have keys outside of the numpad to toggle between modes and provide other functionality
  • Should have mechanical switches because it is not worth doing anything less

This led me to use a block of 4x5 keys and a smaller block of 4x2 keys. I knew that addressable LEDs such as the WS2812B or the SK6812 were a good solution to layer indication at the front, requiring less layout work than installing a multiplexer and several single colour LEDs, and providing a good visual indication of layer state with a single glance. These can be used in the future for displaying some sort of system state of the connected computer.

I chose to use plate mount Cherry MX Black switches in this project. For context, many mechanical keyboards are designed such that the keyswitches clip into a plate, and the circuit board is subsequently inserted onto them from the back. An alternative to these is PCB mount switches which rely on the circuit board for mechanical stability, producing less rigid action but avoiding the cost of a plate. I was building a case anyway, so plate mount was the obvious choice.

Design phase

I began by designing the PCB in KiCad based partially on this guide on GitHub, and I found this blog post on switch matrices very helpful for understanding how the diode arrangement works with the keyswitches and how to draw it.

There are a few comments to be made about that guide: it isn't updated to KiCad 5.x, and the built in KiCad libraries have been improved significantly since 4.x, and it uses a crystal symbol which can result in the wrong pinout using the recommended crystal.

The fact that it suggests using custom libraries for crystals and other common components is a practice I cannot recommend based on what I noticed the instant I went digging through the KiCad library while debugging my micro not communicating over the programming interface (more on this later). That is, that there are multiple different pinouts of crystal, and that is abundantly clear based on the descriptions that are there. In contrast, the custom libraries for keyboard parts only have one type of crystal, which hides this error.

kicad-crystals

While designing the schematic, I found application note AVR042 very helpful for explaining how to design the circuit for the reset circuit, appropriate decoupling and more.

For instance, while reviewing my schematic, some engineers on /r/AskElectronics were unaware of the recommendation to use a resistor in series with the reset switch in order to slow the discharge of the reset capacitor, potentially creating voltage spikes outside of its spec due to the inductance of the reset line.

Manufacture

I chose JLCPCB for getting my PCBs manufactured because they were at least a third of the cost of the other options I looked at, and promised very impressive turnaround times for that price. In all, I spent C$17 on circuit boards, including shipping such that they took 8 days from order to landing on my doorstep. The PCBs turned out quite nice to my untrained eye:

macropad-front-small

macropad-back-small

All components on the board were hand soldered with only slightly less flux than would be used by Louis Rossmann. This was my first project using SMD parts, and I can state unequivocally that 0806 sized parts are more than possible to solder by hand, and 0.8mm TQFP packages are not too bad either. I purchased a T18-S7 well tip in order to drag solder more effectively, which was largely successful, though might work even better with nicer flux.

Magnification was not required for soldering, however it was critical to inspection of the soldering of the microcontroller, which revealed a few solder bridges.

Parts including switches and all electronics were purchased from Digi-Key, who, true to their reputation, had the parts on my doorstep the next day. The bill of materials parts cost is around C$52 with a quantity of 1.

The case and plate was printed in translucent PLA. It could have probably been printed in white and the LEDs would have shown just fine. I designed this case in Fusion 360, which I have over 100 hours of experience with, designing projects such as my team's Skills Canada robotics design (I will write about this as soon as it is not competitively disadvantageous to do so).

Firmware

This was a bit of a problem stage in development to some degree, in particular getting the ISP programmer to work. These all turned out to be hardware and software issues unrelated to the actual ISP programmer. I dodged a bullet by using Linux regularly, because the symptoms of using avrdude on Windows are identical to the symptoms of the crystal not working or the cable being disconnected, which could have been some horrific debugging.

The programmer in question is a Deek-Robot USBTinyISP obtained from Universal-Solder, which is an online shop based in Yorkton, SK carrying many cheap Chinese development boards for a very minimal premium over buying them on eBay. I'd strongly recommend them if you live in the Prairies, because using them saved me several weeks of wait time.

I chose qmk because it was posted somewhere online that it was better than tmk, and it does the job. Currently this part of the project is developed as a fork of the qmk repository, but I can likely push my keyboard configuration upstream.

There are many strong words that could be said about qmk documentation, but I cannot and will not say any of them until I've submitted pull requests to improve it.

I strongly recommend using the qmk bootloader, because it appears to be the only one which allows you to actually get out of DFU mode on keyboard startup, albeit by pressing a key (please tell me if I'm wrong on this!).

I found out only through a reddit post that there is the :production target in the qmk Makefile that allows you to build a full image including the bootloader and the application image which you can flash to the keyboard to bootstrap it. This would be used for example by running make handwired/mech_macropad:default:production where handwired/mech_macropad is the path under keyboards/ for the keyboard you want to compile for and default is the keymap.

Learnings

I learned the hard way to check footprints against datasheets and to make sure that there are no unconnected pins which are not intended to be that way in the schematic. This happened when I had the wrong schematic symbol and footprint for my crystal. I'd like to thank the folks at CrashBang Labs for their invaluable help in debugging this issue.

I need to exercise more care in avoiding getting sticky flux into switches. Thankfully, that was learned on the reset switch rather than a keyswitch.

Many of the earlier tracks on the circuit board design were pointlessly thin, and power tracks could be even thicker than they are. I will consider using polygons for both power and ground more aggressively in future designs, as they significantly simplify routing, reduce resistance, and improve EMI characteristics (which I look forward to learning about in Electrical Engineering over the next few years).

Status

This project works with all designed features, though I need to invent more macros. Currently, I have music playback, volume controls, like/dislike in Google Play Music Desktop Player, and Discord mic mute.

I found a useful trick for these sorts of shortcuts that are not default OS functions is to use modifiers (ctrl, alt, shift) with high F keys (F13-F24 are supported on Windows and Mac, but few keyboards actually implement them, so they will not conflict with any existing shortcuts).

Source availability

This project is open source hardware, published under the terms of the TAPR Open Hardware License. The firmware is published under the GNU General Public License v2.

Firmware

Hardware

Mechanical: I will publish this once I fix some clearance issues around the USB port to avoid requiring a Dremel.

Tags: electronics, firmware, mechanical

Hyper-V Manager throws obscure errors if the target computer calls itself something else than you do

I started testing Server 2019 as a Hyper-V host a few days ago, but getting the GUI manager to connect was a bit challenging. This article will be about as much documentation for me to set this machine up again as it will be instructive.

This machine is non domain joined.

First, name the computer what you want its final DNS name to be with Rename-Computer. Then reboot so you will avoid the issue described in the second half of the post.

Secondly, get a remote shell into it. Enable-PSRemoting, and ensure the firewall rules are allowing connections from the subnets you're OK with remote connections from with Get-NetFirewallRule piped to Get-NetFirewallAddressFilter and Set-NetFirewallAddressFilter.

Next, enable CredSSP with Enable-WSManCredSSP -Role Server and ensure that the appropriate fresh credential delegation, trusted hosts, and permit-CredSSP GPOs are applied on the client. Check also that the WinRM service is running on the client, and if there are still issues with lacking "permission to complete this task" while connecting with the manager, also run Enable-WSManCredSSP with the client role, delegating to the appropriate host.

Then, hopefully, the Hyper-V manager will just connect.


Now, for the problem I had, and as many details as feasible so the next person Googling for it will find this post.

The error that appeared was:

"Hyper-V encountered an error trying to access an object on computer 'LF-HV02' because the object was not found. The object might have been deleted. Verify that the Virtual Machine Management service on the computer is running".

Object not found error

I then investigated the event logs on the target system. In the WMI-Activity/Operational log, I found an error with event ID 5858, and result code 0x80041002:

Id = {8FA5E5DB-34E0-0001-31E6-A58FE034D401}; ClientMachine = WIN-QKHK3OGNV1V; User = WIN-QKHK3OGNV1V\Administrator; ClientProcessId = 2532; Component = Unknown; Operation = Start IWbemServices::GetObject - root\virtualization\v2 : Msvm_VirtualSystemManagementService.CreationClassName="Msvm_VirtualSystemManagementService",Name="vmms",SystemCreationClassName="Msvm_ComputerSystem",SystemName="LF-HV02"; ResultCode = 0x80041002; PossibleCause = Unknown

event5858

When poking around at the mentioned CIM object with Get-CimInstance -ClassName 'Msvm_VirtualSystemManagementService' -Namespace 'root\virtualization\v2', I found that the system name was some randomized name starting with WIN-. So, I renamed it to what it was supposed to be called with Rename-Computer, rebooted, and that fixed the issue.

Tags: hyper-v, Windows Server, PowerShell, Server 2019

Dell XPS 15: "I can't understand why some people _still_ think ACPI is a good idea.." -Linus Torvalds

I got my new machine in the mail, an XPS 15 bought on one of the numerous sales which pretty much happen every couple of days, and while most of the hardware is amazing compared to my previous machine (a beat-up X220), there are some significant hardware issues that need to be worked around. Besides, of course, the fact that the keyboard and lack of trackpoint is objectively inferior to the previous machine.

The first thing that many people may do after booting up a new machine on any operating system is to make sure they got what they paid for, and check detected hardware. So, naturally, I run lspci... and it hangs. I could change virtual console, but it said something about a watchdog catching a stalled CPU core. Fun! Off to Google, which states that it's the NVidia driver, specifically related to Optimus (which, by the way, this video remains an excellent description of). So I blacklist it, and lspci seems to work fine. Next, I install X and all the other applications I want to use, and being a sensible Arch user, I read the Arch wiki on the hardware, which states that the dedicated graphics card will use a lot of power if it isn't turned off.

So, I turn it off. For this, I use acpi_call with a systemd-tmpfiles rule to turn it off at boot. The setup is as follows:

~ » cat /etc/tmpfiles.d/acpi_call.conf
w /proc/acpi/call - - - - \\_SB.PCI0.PEG0.PEGP._OFF
~ » cat /etc/modules-load.d/acpi_call.conf
acpi_call

Next, I get to work doing some programming on it. It was a massive improvement on the previous hardware on account of having a 1080p screen instead of a 1366x768 device-usability-eliminator. However, my terminal-based vim sessions kept getting disturbed by messages such as the following:

kernel: pcieport 0000:00:1c.0: PCIe Bus Error: severity=Corrected, type=Data Link Layer, id=00e0(Transmitter ID)
kernel: pcieport 0000:00:1c.0:   device [8086:a110] error status/mask=00001000/00002000

After looking in the wiki again, I set pci=nommconf in the kernel options. At this point I was entirely unconvinced that the acpi_rev_override=1 stuff was necessary since I got rid of any NVidia software that could possibly break my machine.

Satisfied with my handiwork, I put the machine into service, and took it to school. Naturally, one may want to put a machine into sleep mode if it is not in use. Unfortunately, doing so was causing it to lock up upon any attempt at waking it. Another strange behaviour that I had been starting to notice at this point was that Xorg could not be started more than once a boot due to the same hard lock issue.

As it turns out, this was again the same issue as the sleep, which is fixed by the acpi_rev_override=1 in the kernel parameters. I had been dissuaded by the Arch developers disabling CONFIG_ACPI_REV_OVERRIDE_POSSIBLE at some point in the past, which was what was suggested by an outdated forum post (lesson learned: do more research on things which could easily change), but they reenabled it recently.

So, finally, the situation:

  • Power management appears to work correctly
  • Battery life is incredible (but could probably be hugely improved to "ridiculous")
  • The touchpad is a touchpad, which means it sucks, although it is one of the better ones
  • There is a significant and very annoying key-repeatt isssuee which happens on occasion, some users have reported it also occurs on Windows. It has happened at least 5 times while writing this post.
  • I hadn't noticed this earlier, but the keyboard has a tendency to scratch the screen while the laptop is closed. Since this is a thoroughly modern machine, there isn't really space to just shove a microfiber cloth between the screen and keyboard like I had done with my X220 with missing rubber standoffs.

Would I recommend buying one?

Maybe. For my use case, it made sense since I want to have a dedicated GPU which can be used in Windows for CAD work. The hardware with the exception of the keyboard and trackpad is very nice, especially for the price (a bit more than half what Apple charges for a similarly specced MacBook Pro 15"). If you don't need or want a dedicated GPU, buy another machine. NVidia still has awful Linux problems.

Which machine? Probably a ThinkPad since they have very good Linux support right out of the box. That being said, I acknowledge that Dell has a group dedicated to Linux support on their hardware, and both companies have similar complete lacks of desire to lift a finger with regards to pressuring their fingerprint reader vendor (the same one for both companies!) to release the driver spec.

Since Linus Torvalds provides such excellent material to quote,

The thing is, you have two choices:
 - define interfaces in hardware
 - not doing so, and then trying to paper it over with idiotic tables.

Sadly, Intel decided that they should do the latter, and invented ACPI.

There are two kinds of interfaces: the simple ones, and the broken ones.

<...>

The broken ones are the ones where hardware people know what they want to
do, but they think the interface is sucky and complicated, so they make it
_doubly_ sucky by then saying "we'll describe it in the BIOS tables", so
that now there is another (incompetent) group that can _also_ screw things
up. Yeehaa!

Tags: linux, arch-linux, hardware, laptop, dell-xps-15

Meshmixer: Turn Off Smooth Display

The default display for meshes in Meshmixer is just a bad idea, especially for people who use it as an STL viewer for technical models.

The setting responsible for this silliness is called "Mesh Normal Mode", which as we all know, should be completely obvious to anyone and everyone. Set that to "Face Normals" and it will display without making the model look like an amorphous blob. Alternately, hold spacebar and select the sphere that has vertices as in the picture below.

Setting in the "Hotbox"

meshmixer-setting-fix

Default

meshmixer-defaults

Face Normals

meshmixer-fixed

Tags: 3dprinting, meshmixer

SELinux notes

ausearch -m avc to find denials. If there are none, that's probably because some distro maintainer decided that the denial should be silent:

semodule -DB turns on dontaudit events, semodule -B turns them back off.

When trying to get things to work correctly with audit2allow, skip the 15 minutes of doing things over and over triggering different denials and running audit2allow -M mymodule < fails; semodule -i mymodule.pp by just doing a quick setenforce 0 before doing it once. All of the actions (AVCs?) in creating a file will show up in the log in one shot. Obviously turn on enforcing mode afterwards.

When in doubt, consult the colouring book. Yes, that's real.

Tags: linux, selinux

MS Documentation sucks (or how I got my VM hostnames to be set automatically from kickstart)

I wanted to automate my linux VM deployment on my Hyper-V based lab infrastructure. One small flaw: while DHCP does automatically update DNS, it does not do too much when your VM is named "localhost". I wanted to make the fedora deployment completely automated... which it is after I wrote a kickstart, except you can't get into the new box because you can't find its IP address.

I wrote a small tool to deal with this issue:
https://github.com/lf-/kvputil

You want the variable VirtualMachineName in /var/lib/hyperv/.kvp_pool_3.

Documentation that took way too long to find:
https://technet.microsoft.com/en-us/library/dn798287.aspx

Tags: hyper-v, linux

Launching PowerShell using the Win32 API

I was working on a personal project in C on Windows when I stumbled upon a really strange roadblock: a PowerShell instance would not actually run the script given to it when started via Windows API but it would when launched manually from a cmd.exe.

Eventually the realisation came to me: PowerShell doesn't like the DETACHED_PROCESS option for CreateProcess(). I have no idea what it was doing with it there, but it didn't involve actually working.

I changed it to CREATE_NO_WINDOW and all is fine in the world.

Tags: windows, PowerShell, win32