2021 Nov 22
2021 Nov 19
  • 202111190938 09:38 Nov 19

    Redesigning the thing. I do need more torque after all, and I need a better hinge joint. The one I’ve got is too stiff.

2021 Nov 18
  • 202111182116 21:16 Nov 18

    Curse my inability to take notes. I always think I’ll come back to whatever-it-is soon enough that I don’t need to. I’ve only just managed to get back to one of my projects in time to figure out what on earth was going on.

    There is a parallel universe where I don’t have this problem. In that universe I’m fucking rich.

2021 Nov 3
  • 202111031759 17:59 Nov 3

    Fell off the wagon of posting. Must do better. Anyway…

    Just kicked off a share price reading update. I’m somewhat concerned that the list of symbols is out of date, so I’ll need to get a new list fairly soon. Not quite sure how I’ll integrate that into what I’ve got, but it shouldn’t be too hard to figure out.

    The mirror project is 90% done, in that I think I’ve got the basic unit design finished. I’m printing off my second instance now, and if that works I can print off as many as I’ve got motors for. I’ve not got a backing board for them yet though, so that’s got to be next. Then there’s the small matter of where to put it. I’ve also not thought too much about what pattern they should boot up into, if any. I’m driving the i2c bus from a pico, but it might make more sense to switch to a zero (now I’ve got… several… lying around) so I can have more smarts built-in and control it over the network.

    Speaking of which, I’m not sure why the new Zero 2’s don’t bring up the network interface when booted from a Zero’s SD card, but they don’t. There’s an added annoyance there in that the source code I want is on the SD card, it boots up, but I can’t remember the credentials I reset the pi account to and the other account on the card is ssh-only. Have to pull it off manually.

2021 Oct 20
2021 Oct 17
  • 202110172323 23:23 Oct 17

    In case I lose it, some details about my current Neptune 2 printer gcode:

    G28 ; home all axes
    M104 S170 ; heat the nozzle
    M140 S60 ; heat the bed
    M109 S170 ; wait for nozzle temp
    M190 S60 ; wait for bed temp
    G28 ; home all axes
    G29; run bilinear probing
    M500 ; Save Settings To epromm
    M420 S1 V1 ; Restore and report Bed Mesh
    M140 S{material_bed_temperature_layer_0} ;Start heating bed
    M104 S{material_print_temperature_layer_0} ;Start heating extruder
    M190 S{material_bed_temperature_layer_0} ;Wait for bed to reach temp before proceeding
    M109 S{material_print_temperature_layer_0} ;Wait for extruder to reach temp before 
    G92 E0 ;Reset Extruder
    G1 Z10.0 F3000 ;Move Z Axis up
    G1 Z4.0 F3000 ;Move Z Axis up
    G1 X1.1 Y20 Z0.28 F5000.0 ;Move to start position
    G1 X1.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line
    G1 X1.4 Y200.0 Z0.28 F5000.0 ;Move to side a little
    G1 X1.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line
    G92 E0 ;Reset Extruder
    G1 Z2.0 F3000 ;Move Z Axis up
    

    Broken down into sections, that’s:

    G28 ; home all axes
    

    This is an initial “get somewhere known” move. With the BLTouch fitted it leaves the head in the middle of the bed after the Z axis homes. Not great, not terrible. I might swap this out for a G28 Z; G28 X Y so it doesn’t leave the head in the middle of the bed for the next bit.

    M104 S170 ; heat the nozzle
    M140 S60 ; heat the bed
    M109 S170 ; wait for nozzle temp
    M190 S60 ; wait for bed temp
    

    This is pretty much what the Prusa MINI does, so I nicked it on the basis that it’s likely to be sensible. I think the logic is that you mostly want the bed up to temp because that’s the bit that’s going to expand the most, but you also want the nozzle hot in case it affects the sensor. I’m not sure why 170 degrees though - it might just be “eh, close enough” for most PLA brands without causing drooling.

    One other note on this: I quite often see examples where the middle two commands in this sequence are the other way round. That is, heat the nozzle, wait for the nozzle, heat the bed, wait for the bed. That’s obviously slower than it needs to be, there’s no reason you can’t heat both at once and there’s no reason you should need to do one wait before the other.

    Moving on…

    G28 ; home all axes
    G29; run bilinear probing
    M500 ; Save Settings To epromm
    M420 S1 V1 ; Restore and report Bed Mesh
    

    This G28 is a re-home after heating everything up, and is for precision. Mostly X and Y, because we’re about to re-do Z. I have no idea if it has a measurable effect.

    G29 is where the BLTouch magic happens. When this has finished, the screen on the Neptune 2 will show the Z-adjust screen, which will remain there for the course of the print. Do not press Back, or the head will re-home mid-print. It should carry on with the print after that, but you’ll get a stop/start blob wherever the head happened to be and you don’t need to bother with anything else on the screen because you’re driving everything through Octoprint. I’m not strictly certain that the M500; M420 S1 V1 sequence is necessary after a G29 except that it might come in handy in a post-power-outage restart.

    M140 S{material_bed_temperature_layer_0} ;Start heating bed
    M104 S{material_print_temperature_layer_0} ;Start heating extruder
    M190 S{material_bed_temperature_layer_0} ;Wait for bed to reach temp before proceeding
    M109 S{material_print_temperature_layer_0} ;Wait for extruder to reach temp before 
    

    Again taking our cue from the Prusa MINI, this is where we get up to printing temperature. And again, these commands are in a different order than you’ll see in online examples because those examples haven’t had much thought go into them. The variable names can be found in a file which, on my machine, lives at C:\Program Files\Ultimaker Cura 4.11.0\resources\definitions\fdmprinter.def.json.

    Then we have the swipe to charge the nozzle:

    G92 E0 ;Reset Extruder
    G1 Z10.0 F3000 ;Move Z Axis up
    G1 Z4.0 F3000 ;Move Z Axis up
    G1 X1.1 Y20 Z0.28 F5000.0 ;Move to start position
    G1 X1.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line
    G1 X1.4 Y200.0 Z0.28 F5000.0 ;Move to side a little
    G1 X1.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line
    G92 E0 ;Reset Extruder
    

    I’m not 100% happy with this: the feed rates are pretty arbitrary, it’s close enough to the edge that I worry about it missing the bed if I don’t align it well, and I don’t think there’s any reason for the second line, but it’s not something I’ve changed from the default yet.

    The first G92 E0 tells the extruder that no matter where it thinks it was starting from, it’s now at position 0; the second one at the end is so that the remainder of the print’s gcode doesn’t need to take the swipe extrusion into account.

    And that just leaves us with…

    G1 Z2.0 F3000 ;Move Z Axis up
    

    This leaves the head 2mm above the bed so on the off chance there’s something in the way (maybe some crap on the bed from a previous print, if I’ve not quite cleaned it well enough) the head doesn’t crash into it as soon as the print proper starts.

    And that’s it. I’ve not messed with the end code, but I don’t think there’s much I’ll want to do other than lift the Z axis further clear on completion. It really bugs me how printers just leave the head in the way of getting the bed off. But that’s a problem for another day.

2021 Oct 6
  • 202110061629 16:29 Oct 6

    Docker gets very confused if you have a depends in your docker-compose.yml. In some circumstances (possibly involving profiles) it tries to create a new network, but that network doesn’t exist.

    Take out the depends.

2021 Oct 5
  • 202110052256 22:56 Oct 5

    So I think the bit of the systemd picture I was missing was the way that there’s a system systemd session, and separate per-user sessions that get started per login. Those sessions are what systemctl --user commnunicates with.

    What I want to be doing is a. to run raspistill as a system service; b. to run that system service as my non-root user; c. to send that service signals from a script running under my non-root user’s crontab; d. not to need authenticating to do so.

    It’s not clear that there’s any well-supported way to do this. The right way to send signals to a systemd service is with systemctl kill, but if you’re interacting with the system systemd session that needs authenticating even if the service is running as you. For now I’m just running pkill -USR1 raspistill from my crontab and it all works fine.

2021 Oct 1
  • 202110010756 07:56 Oct 1

    To set a user service to start at boot, you need:

    • WantedBy to by default.service
    • sudo loginctl enable-linger $USER
    • systemctl --user enable <service> - note you don’t need sudo here
    • the unit file to be in /etc/systemd/user, not /etc/systemd/system
  • 202110010719 07:19 Oct 1

    Wondering if R2 will support HTTP ranges. That could be intriguing. If R2’s latency is better than S3’s, it might be reasonable to netboot from it. It might also work to proxy an R2 object as an NBD device, which isn’t something you could reasonably do with S3, with its 230ms pre-range-request latency.

2021 Sep 28
  • 202109282248 22:48 Sep 28

    Fucking systemd. Or polkit. Same midden.

    I have a systemd unit. It runs as my user. It is a single process which does not fork. I want to send a SIGUSR1 to that process. systemctl kill is the “right way” to do this.

    Why, then, when I am signed in as me, and the service is running as me, does systemctl kill ask me to authenticate with a damn password before deigning to pass my signal on to my process? For fuck’s sake.

2021 Sep 27
  • 202109272015 20:15 Sep 27

    Tired. Tired of it all. Tired of waiting for it to be different. Tired of the treadmill. Tired of waking up. Tired of going to sleep, to work, to sleep. Tired.

2021 Sep 20
  • 202109201452 14:52 Sep 20

    There seems to be a gap in the market for tech books. What I want is something that’s got the plain-speaking “just do this, anything else is wrong” approach to code architecture of BM’s Clean Architecture, combined with the broad knowledge/systems architecture methods of DDD. What I want is something biblical, in the sense that it is definitive, and requires no thinking about other than whether to accept it whole or not.

    Code and system architecture in an organisation where these things have not previously had a framework to drive them will be a hodge-podge of varying quality with little to no consistency, as a result of which decisions about the basics, and about who owns which parts of the scope, and even what “scope” is, need to be rehashed on every project. We end up with decisions in different places being made on the basis of who is involved, not according to any overarching plan, and lacking a framework to render some set of decisions pre-made, we relitigate the same problems over and over again, wasting time on irrelevancies instead of spending it on questions that could move us forward. We complain “there should be a plan for XYZ” when, with an appropriate framework in place, that plan would write itself.,

    Domain-Driven Design is a particularly strong framework for dividing up an organisation’s activities. It doesn’t assume a greenfield, instead starting from where you are today and prescribing transformations for how to get to where you want to go. It provides a clearly-defined language for talking about software system construction: if you agree that you are doing DDD, then you agree on the definition of Bounded Context, Entity, Ubiquitous Language, and so on. It defines a Ubiquitous Language for the sorts of systems it teaches you how to build, in other words.

    It is also well-contained in print publications which have a “biblical” status. Eric Evans' first book lays out the core, Implementing Domain Driven Design shows how to turn that into code, and Domain Driven Design Distilled gives a quick primer to get someone up to speed. Between those books, it is possible to say “if you’re not doing X, you’re doing it wrong.”

    This is similar to another architectural approach, risk-based architecture (from Just Enough Architecture, by Fairbanks), where the you could equally say “yes, this is right” or “no, this is wrong” according to that approach, but the definitions are more woolly, more subject to interpretation, and provide less of a solid scaffold to build a team process around. For a team in a situation where there has historically been little in the way of coordinated architectural thinking, it does not supply the certainty, the just-do-this formula of DDD.

    Where DDD ends and code organisation starts is where I see the gap. Clean Architecture dovetails nicely with DDD. As DDD supplies the domain boundaries and tells you which responsibilities lie with which software artefact and with which team, Clean Architecture tells that team how to implement the domain, and just as importantly how to organise the code around that domain so that the whole remains easy to change. And, just as with DDD, there is a print publication which has canonical status.

    CA is not the only code architecture system existing in that part of the ecosystem. Hexagonal Architecture, and Ports And Adapters both look very similar, and could probably be employed by a team needing them with just the same success as CA. However, neither of them has what CA has, at least to my knowledge: a canonical reference to say “write your code this way”. Indeed, there are areas where the CA book itself leaves too much to interpretation: the later chapters would benefit massively from code examples to show the way. BM says that the architecture should “scream” but doesn’t really demonstrate what he means by that. The gaps are small enough that a motivated team would be able to fill them in, but I was disappointed that they existed at all.

    Why not prescribe a combination of DDD and CA, then? Well, the main problem is that BM is a bin-fire of a human being. While his writing is persuasive, he has a tendency to treat approving reviews of his text as approving views of himself. He has a voice of authority which, to be perfectly clear, is good for absolute beginners who, at their level on the Dreyfus skill acquisition scale, do need to be told what to do without thinking too hard about it. This gains him followers who can be forgiven for not knowing any better, and who follow him well past the point that they benefit from his guidance. So, when thinly-disguised racism and misogyny rear their heads, the harm he does is magnified.

    This militates strongly against recommending CA to an organisation containing (or hoping to contain) minorities: if use of the system can be taken as approval of the behaviour of the author, as we have reason to believe it would be by the author himself, no such use can morally be justified. This is additionally problematic when BM holds himself up as an arbiter of programmer ethics.

    Compare this to Hexagonal Architecture. The author, designer, progenitor, source of the material Alastair Cockburn seems to take a far more flexible, discovery-led approach to team-level system design. This is unfortunate in the sense that it assumes a certain level of maturity in an organisation to be able to take on the message and run with it. Lacking the declamatory tone of CA means that the message is always subject to a certain amount of interpretation, to a far greater degree than CA. There is rarely a sense of “this is the right way”, more “this is a way which works for me”. That is a far more realistic and humane tone in the widest application, but for teams at the level where they need preaching at to get past the first on-ramp, it lacks punch, it lacks the crystal clarity of “there is one true way.” Over time, they may learn to relax the constraints but right here, right now, if they need certainty, HA doesn’t have the artifacts necessary to provide it.

2021 Sep 15
  • 202109150914 09:14 Sep 15

    The technical world would be improved immeasurably if the words “simply”, “just”, and the expression “why don’t you” were expunged from our collective consciousness.

2021 Sep 14
2021 Sep 13
  • 202109132109 21:09 Sep 13

    Figured out a FreeCAD workaround: use construction lines with fixed angles to stop parametric models from breaking when you change scales. Lines aren’t symmetrical, so a fixed angle forces a single solution where more than one might be otherwise allowed.

  • 202109131336 13:36 Sep 13

    I wonder why colmap is so slow at feature matching. Naively I can see how it would be O(n^2), but surely you can do better than that without needing silly hardware?

2021 Sep 12
  • 202109121503 15:03 Sep 12

    Copper plating of propelling pencil threads for use as electrodes: yes, this works.

    What’s working so far for me is:

    1. Drop a load of copper sulphate into a jar with some tapwater. It’s a saturated solution.
    2. Take two squares of copper slug tape, and fold then in half twice.
    3. Put each folded square into the croc clip on both polarities of the power supply.
    4. Insert one propelling pencil lead into the folded square if the positive supply. The folded square is there to be a conductive wrap that stops the croc clip from crushing the lead, so it should sit snugly but not too tight. We’re using a lead as the anode so it doesn’t get destroyed in the electroplating process.
    5. Put the other propelling pencil lead into the negative supply, in the same way. This one will end up getting plated.
    6. Suspend the positive pencil lead into the copper sulphate solution.
    7. Turn the power supply on. 3V should do the trick. You don’t want it too high: a high voltage makes the copper come out of solution too fast, and doesn’t make a good surface.
    8. Take the negative pencil lead, and hold it by the insulated part of the negative supply. This is now the cathode. Being careful never to let it touch any part of the anode, shake it back and forth in the solution for about 5 minutes. You do want the cathode and anode within a couple of centimetres of each other. The purpose of the movement is both to clear the surface of the cathode of any buildup of anything that might get in the way, and to make sure the bulk of the solution is getting circulated. You should see the cathode change colour fairly quickly.
    9. Take out the cathode after 5 minutes, and carefully wipe off any liquid. It won’t be shiny: it should be a diffuse, pale pink.
    10. Switch off the power.

    The point of this exercise is that now the plated end of the cathode will take solder, so you can attach a copper wire to it securely.

    The other end can be sharpened on a piece of paper.

2021 Sep 11
  • 202109111818 18:18 Sep 11

    Seems like copper plating a carbon cathode at anything over 5V builds up black copper solids around the cathode, rather than coating with solid metal. I got a very small shiny copper tip on the cathode, but the length of it was untouched.

    Lower than 5V gives quite low current (<0.2A) with the cobbled-together parts I’ve got, so I probably want to sort out the geometry.

  • 202109110335 03:35 Sep 11

    Threads for 3D Printing in FreeCAD

    A bit nuanced, this.

    1. Basic inner thread construction (like on a bolt)

    We’ll construct the shank of the bolt, the thread, and the head of the bolt, in that order. Assume for this description that we’re modeling a bolt sat vertically on its head, so the thread is spinning up around the Z axis.

    We’ll Use a helix cut, not a helix addition, to make chamfers low-effort.

    Make a rectangle sketch on the XZ plane to define the shank profile. We’re going to define the volume with a revolution, so the lower left corner of the rectangle should be on the origin, the height of the rectangle should be Thread.Length, and the width should be the outer radius of the thread, which we’ll call Thread.OD/2. Create a revolution with the sketch.

    Make a new sketch to define the thread profile. Pay attention to where the seam of the shank geometry is. You want your thread profile sketch not to interfere with it, or the geometry kernel gets all confused because points and lines will be coincident and it won’t know what’s supposed to be inside and what’s supposed to be outside. In our case that means putting the sketch on the YZ plane.

    The upper edge of your thread cut profile should be coincident with the lower edge of the bottom end of the shank.

    The dimensions for the thread profile should be:

    • Small inner vertical height: Thread.Pitch * 1/4
    • Angle between diagonals: 60 degrees
    • Vertical profile height: Thread.Pitch * 7/8
    • Additional overlap outside the shank: 2mm

    The radius of the shank is Thread.OD/2, but we should set the distance from the Z axis to the outer corner of the diagonals of the profile to Thread.OD/2 + 0.01, so that those corners miss the surface of the cylinder and the geometry kernel doesn’t have to work too hard.

    Make a helix cut from the thread profile sketch. Set the pitch to Thread.Pitch. Set the length to Thread.Length + Thread.Pitch, so that the full length of the shank is threaded. Note that if we had modeled the head of the bolt first, this wouldn’t work, because we’d be cutting into the head at this point and have to make do with a portion of the shank unthreaded.

    The thread is now defined.

    To define the head, create a new sketch on the XY plane and draw a hexagon larger than the diameter of the shank. Pad it by 5mm in the reverse direction.

    If you want to chamfer the end of the bolt, return to the shank profile sketch. Change the top right corner of the rectangle to have a 45-degree angle cut out, and close the sketch.

    2. Basic outer thread construction (like inside a nut)

    The process for an outer thread is as for an inner thread, with these changes:

    • The shank profile should be the profile of a tube with an inner diameter of Thread.OD - Thread.Pitch * (sqrt(3)/2) * 5/8) * 2 + 0.4 * 2. We can call this Thread.ID + 0.4 * 2. The term Thread.Pitch * (sqrt(3)/2) * 5/8 is the thread depth. The 0.4 term is the clearance between the inner and outer threads: I’ve found that a clearance of 0.2mm on printed parts is enough for two parts to interface without too much friction, and if you do the trig calculations you’ll see that with an angle of 30 degrees to the horizontal, you double the clearance: a face separation of 0.2mm requires a horizontal separation of 0.4mm. The outer diameter of the tube can be whatever is convenient.
    • The profile has the opposite orientation to the prior, with the small edge pointing outwards.
    • The profile dimensions differ:
      • Small edge length: Thread.Pitch * 1/8
      • Vertical profile height: Thread.Pitch * 3/4

    We set the distance from the Z axis to the inner corner of the diagonal edges to be Thread.ID/2 + 0.4 - 0.01

    3. Actual bolts

    Threads as part of actual bolts may need to be printed horizontally, rather than vertically, so that the printed layers don’t present weak planes normal to the length of the bolt. It’s fine to print them on their side, with a flat above and below.

    4. Outer threads that don’t collapse

    Continuous outer threads tend not to print well, because the strands of filament detach from the thread as they contract and cool. To prevent this, cut sections out of the thread so that it’s composed of many short lengths, rather than long ones. You want to cut 8 vertical slots through all the threads; the number isn’t critical, but what you’re aiming for is to make each remaining section of thread more closely approximate a ledge than a circle. It won’t completely compensate for poorly adhering overhangs, but it’ll vastly reduce the problem with the number of new anchor points it introduces.

    5. Sneaky part geometry

    If the threaded part has any other complicated geomemtry (like, say, a gear cut out of part of it) then the geoemtry kernel seems to get bogged down. It ends up trying to calculate whether the thread and that other geometry intersect at all if they’re part of the same body, and often we can avoid that by being a little sneaky.

    Take our bolt, for instance: imagine, for whatever reason, we wanted the head of the bolt to be in the shape of an involute gear from the FCGear workbench. There are reasons you might want to do this: you could imagine a worm drive laid out like this. Anyway, what I’d do is model the bolt as just a cylinder for the shaft, and the head of the bolt. Only I’d make the shaft diameter equal to the inside diameter of the thread. Then, in another body, I’d make a threaded sleeve that was a tube, of inside diameter just fractionally less than the thread ID, and outside diameter the thread OD, and cut the thread out of that. Then assemble the bolt and the threaded sleeve in Assembly4.

    Now, here we can be a little sneaky. Because of the way slicers work, if we’re modeling for 3d printing, we don’t need to union the sleeve and shaft together to make a single solid. Just by having them overlap, when we export an STL and slice them in the slicer, they’ll get printed as a single body with exacty the same paths as though we’d done a boolean Union. That can save us doing geometry calculations we don’t need.

  • 202109110258 02:58 Sep 11

    It seems like the instructions and DSDT patches here for getting suspend to work properly on a Lenovo Yoga Slim 7 finally work on mainline kernel 5.14.2. Took a few months, but this laptop now doesn’t randomly barf and reboot when waking from sleep.

  • 202109110250 02:50 Sep 11

    Insomnia wouldn’t be so bad if I could convince my brain to actually do something useful with all these extra 2:30am hours of consciousness. I’ve got all these trains of thought running round my head - work, printing, more work, books I can’t convince myself to open, code I want to write - and none of it will come out. None of it.

2021 Sep 10
  • 202109102056 20:56 Sep 10

    Hugo convenience script that does a commit and deploy. This drops the activation energy for posting; just run $ bin/h, it launches an editor, and if you quit without triggering an error code, the post is posted.

    In vim, quitting with an error code is pronounced :cq.

    #!/usr/bin/env ruby
    
    def xexec(cmd)
      system cmd
      fail unless $?.success?
    end
    
    datestr = `date +%Y%m%d%H%M`.strip
    filename = "posts/#{datestr}.md"
    system "hugo new #{filename} --editor #{ENV['EDITOR']}"
    
    if $?.success?
      xexec "git add content/#{filename}"
      xexec "git commit -m '#{filename}'"
      xexec "make deploy"
      xexec "git push"
    else
      xexec "rm content/#{filename}"
    end
    
  • 202109102045 20:45 Sep 10

    Thinking about an engineering leadership book review/summary series. Start from the Unicorn Project and work out, in the order I encountered them, pretty much. Most of the management books I’ve read have a very small core that can be compactly summarised. Not to say they’re overwritten - often if the ideas are unintuitive, you might need motivating examples or in-depth explanation, or examination from different perspectives - but understanding that there is a common core, a shared philosophy between several lines of thought across separate disciplines that doesn’t so much go against Taylorist or McKinseyite management theory as completely upend it and show it for the inhuman and inhumane practice that it is, does not require a full exposition.

  • 202109100909 09:09 Sep 10

    Ok, looks like etag on; does what I need. Might fiddle with cache control more later, but I’m getting posts rendered out in 25ms now. That’s tolerable; almost all of it is network transit.

    ab reports 2.4ms per request across 1000. I’ll live with that.

  • 202109100905 09:05 Sep 10

    Nginx cache control. Either etag on; fixes everything, or… not. I’m not sure why I’m getting cached content by default that ignores changes on disk, but it’s very annoying. Restarting nginx solves it, but that’s overkill for static file changes.

2021 Sep 8