Category Archives: Technology

3D Printer: Multi Gantry Printer

State: brain storming, early drafts

MG-MIEX (Multi Gantry & Multiple Independent Extruders) [early draft]

Updates:

  • 2024/12/20: early idea to add tool changer as well
  • 2024/12/16: adding more details with multiple extruders on same X gantry
  • 2024/12/06: early ideas about single layer segmenting
  • 2024/12/05: starting write-up

Introduction

While working on theoretical side of parallel 3D printing, I used the example of multi-gantry or Multi Gantry (MG) setup where a single bed is shared in Y – here a few rough sketches:

Gantry Details

There are several challenges, such as the Y- and the XZ motion system and making the thickness of each gantry as thin as possible to have as little “dead” or “unusable” space as it adds up and also limits the printable XY space.

Y motion system

We can share a single rail where each gantry rides hence Common Y Rails (CYR), then they can’t change the order, in that case the belt routing becomes the main issue of concern – whereas when each gantry has its own rail hence Dedicated Y Rails (DYR), and each gantry has a distinct height they can change the order (lift Z to the top and move over the lower one – hence each gantry has a different Z height, but with the filament and cabling the entanglement needs to be closely observed).

XZ motion system

We require to have two positioning axes to integrate here, the X and Z:

  • two independent motion systems (X axis = 1 motor, Z axis = 1 motor)
  • Core XZ system also using two motors, but having XZ motion combined
Jon Schone’s (ProperPrinting) 2 Gantry System: X & Z axes with each their own motor, sharing a single Y rail

Slicing for Multi Gantry System

As I realized earlier, it makes no sense to statically segment Y-space for each gantry (e.g. Y-size/n) although it would make coding easier, but seamless printing would be impossible – in reality, we need slightly overlapping spaces to achieve zero space seams where the gantries print for the same piece, so we naturally choose flexible non-overlapping operations.

Layer Segmenting

Obviously one of the simple optimization is when we are looking at a single layer, and segment it area-wise to n-amount of gantries. For sake of consistency, a wall is assigned to a single gantry to have a seamless wall/perimeter.

Y Segmenting Single Layer for 4 gantries & nozzles (red, green, blue & yellow)
  • Infill areas are Y segmented
    • each area (e.g. A1-A4) is the same to distribute work equally
  • Walls/perimeters are assigned to a single gantry
    • if walls intersect each other in Y, those are printed sequentially not in parallel to avoid Y collision

The layer segmenting approach makes it rather easy so all nozzles have the same Z, thereby segmenting print jobs becomes easy as well.

Mixing Multi Gantry (MG) with IMEX (IDEX & ITEX etc)

We can also mix Multi Gantry (MG) with IDEX (Dual) or ITEX (Triple), or in general Independent Multi Extruders (IMEX), something like this:

Multi Gantry with IDEX: Independent Dual Extruders
per single XZ gantry
Layer Segmenting of Multi Gantry (4: red, green, blue, yellow) with IDEX (2) per Gantry

And like-wise segmenting layer areas in Y as before, and additionally segment in X for each printhead on Independent Multi Extruders (IMEX), in this example it’s IDEX (Dual).

As IMEX looks like scaling print parallelism further, one has to be aware of the spatial overhead for each printhead, e.g. a single printhead occupy ~40mm width in X, and similar in Y.

MG-MIEX 3×2

Early draft with more details based on Ashtar M, extended to MG, with Common Y Rails (CYR), with IDEX:

Challenges

  • 1) When doing a MG-MIEX 3×2 (3 gantry with 2 extruders each):
    • X: 3*2 motors = 6 motors
    • Y: 3 motors
    • Z: 3 motors
    • E: 3*2 motors = 6 motors
      we end up 6+3+3+6 = 18 motors to coordinate & control.
  • 2) Slim design of the gantry to reduce dead-space in Y.
  • 3) Slim printhead design to reduce dead-space in X.
  • 4) Slicer requires to coordinate 6 printheads (e.g. T0-T5) in non-colliding and efficient way.

Solutions

Slicer

A dedicated slicer is required which segments each layer into printable areas A1 to An whereas n is the amount of gantries, and those areas might be further segmented by m-amount of printheads on the same gantry, so they share the same Y position but can have distinct X position but not switch order or collide.

As proposed above, a single printhead of a gantry is then assigned to print within the same Y-segment the walls/perimeters to have a clean wall, hence, the infills are distributed among all the printheads.

Firmware & Controller

The controller with its firmware we have a few options:

  • Duet3D/RRF: Multiple motion systems
    • M400: wait for current moves to finish (when using T<n> with G1s together, applies to X, Y, Z, and also E)
    • M596 P<n> selects motion queue, prior each G0 or G1
    • M597: collision avoidance (at firmware level)
    • M598: sync up multiple motions (faster waits for the slower)
  • Klipper
  • Marlin
    • one controller per gantry, orchestrating between controller needed
    • simple setup, reliable

As the operation of the gantries are overlapping, they cannot have relative position, but rather have absolute Y position (relative position used to detect collision), the same for the X position.

Adding Tool Changer (TC)

In order to support a tool changer, the printheads need to reach a common position in order to deposit and pick up tools. In case of a single Y rail setup this seems at first sight challenging, only a dedicated tool change per gantry, at a particular a YZ position for example. The multi Y-rail setup allows any gantry to reach a common region in YZ, and so any printhead could deposit and pickup a tool from the common toolset.

References

Parallel 3D Printing / Additive Manufacturing – Part 1

Updates:

  • 2024/12/09: ready for publication
  • 2024/12/04: completing first table on printheads & nozzles
  • 2024/07/30: starting write up

Introduction

When depositing material through a nozzle, the variables to compose a workpiece depends on the amount of nozzles and their operational spaces – let’s lay out the different methods which gives us the foundation to tackle then parallel procedures in the next part in the series.

Printheads, Nozzles & Operational Space

PrintheadsNozzles per PrintheadNozzle Size [mm]Layer Height [mm]MaterialOperational Space
Single nozzle FDM/FFF110.1-1.00.1-0.6Polymer (PLA, PETG, ABS)100%
Dual nozzle FDM/FFF aka IDEX210.1-1.00.1-0.6Polymer (PLA, PETG, ABS)2x 50%; horizontally separated
Duplex F2210.1-1.00.1-0.6Polymer (PLA, PETG, ABS)2x 50%; vertical separated
CM3P Dual Conical210.1-1.00.1-0.6Polymer (PLA, PETG, ABS)2x 50% of negative cone
Resin SLA (UV Laser)110.1500.050-0.150Resins100%
Resin MSLA (UV & LCD)150M-100M0.020-0.0400.050-0.150Resins100%
Quantica NovoJet1960.050Resins100%
Stratasys J55 PolyJet1192*)0.2*)0.18Resins100%
Selective Laser Sintering/Melting1+10.10.05-0.10Polymer or Metal Powder100%
  • Stratasys J55: nozzles & nozzle size based on J850 specs, J55 details specs seem not publicized (2024/12)

Print Base

A print base is where the nozzle can extrude on. For the first layer, there is the print bed, after the first layer the workpiece or support structure can be build upon. One can alternatively use a stabilizing medium like silicon and extrude in such liquid medium which operates as bed or foundation like Rapid Liquid Printing (RLP) does:

The extruded material just has to stay where it was put, either a solid bed or a medium which prevents it to float out of position, or as traditionally printed on a print bed or base, very similar does Xolography where the solidified resin stays put as well.

Massive Parallel Nozzles Printhead

Resin printing with a printhead may have hundreds or even thousands of nozzles, yet, they share the same operational space, but due the parallel setup the print speed multiplies direct with the amount of parallel nozzles on the same printhead.

Xaar 128 printhead printing high viscous material

Massive Parallel Nozzles: MSLA

As mentioned above, we can also view Masked Stereolithography (MSLA) resin printing as a massive parallel nozzle setup, where each pixel is either an active or inactive nozzle depositing a voxel.

Anycubic Mono M7 Max MSLA Setup

Separated vs Shared Operational Space

Disclosure: I have been contracted to work on the Duplex F2 software stack (2022-2024).

Let’s take a look at the Duplex F2 printer where space is separated vertically, or the CM3P dual conical printer where the cone space is separated horizontally, or the Multi Gantry 3D printer by Proper Printing (Jon Schone). We have two printheads which never collide due their separated operational space, the firmware is simple and path planning is simple, both heads pretty much can operate independently.

When using more than two printheads it is beneficial to share the operational space, yet assume 6 or 8 printheads, each printhead needs rods to keep the printhead and position and orient the nozzle(s), so overlapping operational space requires extreme well planned tool paths avoiding any collision of the printheads.

Regular Operation Space Separation

We can segment or separate the space evenly or according the reach of the printheads, and each separated space can be printed without colliding. Yet, in reality the printheads mounts limit that operational space into slightly smaller spaces, but ideally:

nvolumes = volumetotal / volumeprinthead

If the individual printhead volumes aren’t regular, then we end up with arbitrary amount of printheads to cover a given print volume:

volumetotal = sum( volume1..n )

In reality, we require (slightly) overlapping operation to get seamless operation, so the “regular operation space separation” is only theoretically, but not practically.

Overlapping Operational Spaces

When the printheads can reach each other operational space, they become overlapping and controlling tool path generation needs to take care no collision is occurring (same place at the same time).

The Static Non-Overlapping Operation has static defined operational spaces where the operators can function – it’s quite obvious such solution is impractical, as in real life there would be space which cannot be reached, a kind of blind seam not reachable by either operator.

The Flexible Non-Overlapping Operation is flexible defined operational spaces, in the illustration above those spaces are co-dependent.

The Static Overlapping Operation is when those operational spaces are overlapping, yet, prefixed or static operational spaces.

The Flexible Overlapping Operation is flexible operational spaces, yet due the nature of the setup these operators cannot occupy the same space at the same time which would result in physical collisions.

Now, the last part of the last sentence may sound obvious to even mention, but bear in mind you can have two projectors shining light into a resin bath, and expose and solidify a 3D model, then these two lights acting as operators indeed occupy the same space at the same time as part of their function. So, the operators functioning with light can occupy the same space at the same time, whereas solid operators, such as robotic arms, cannot.

Print Speed in Parallel Setups

total print speed [mm3/s] = nprintheads * nnozzles * vextrusion [mm3/s] * parallelfactor

whereas parallelfactor is 1.0 if printheads can print parallel, or is less if the operational space is overlapping and preventing printheads to operate parallel thereby.

~ * ~

“Parallel 3D Printing / Additive Manufacturing” Part 2 follows later (will be linked when published)

References

3D Printer: 4-Axis Polar Printer (Joshua Bird)

Updates:

  • 2024/12/03: starting write-up, and published it

Introduction

Joshua Bird announced his 4-axis Polar CoreXB Printer December 1st 2024 on Reddit and YouTube:

Beside the hardware, he also implemented the required software and released it on github as “Radial Non-Planar Slicer” – an impressive undertake and success in one go.

4-Axis Linear Head(XZ), Rotational Head(Y)Bed(Z)

Even though I was impressed by the 4-axis RTN by ZHAW I wrote about, I abandoned the idea of 4-axis setup, as I considered it too limiting. Joshua’s approach to use a Z rotating bed, and the nozzle rotating ~180° or -90°.. +90° in Y-axis implements a polar (coordinate) printer.

He has a fixed Z-rotational center on the bed, and variable head Y-rotation in X. By keeping the Y-rotation (B) at 0° (vertical), the printer can print Z-planar cylindrical as well.

It can print XY Z-planar like a 3-axis 3D printer by mapping the XY coordinates into radial/cylindrical coordinates as well, so let’s summarize quickly what can be done:

  • polar coordinates, spherical slices
  • cylindrical coordinates, Z-planar cylindrical slices (fixed Y/B axis at 0°/vertical)
  • cartesian coordinates, mapping XY into radial/circular coordinates and fixed Y/B axis at 0°/vertical as well

So, this setup is quite powerful – and much better than the 4-axis RTN and also my 5-axis PAX setup, where the cabling and PTFE pipe for the filament prevent 360° Z-rotation / C-axis. Because Joshua’s has the Z/C-rotation on the bed, it’s fully continuous (or use a slip-ring to deliver power for a heated bed).

Dual Z Fixed X-Axis: Bad Idea?

Joshua’s original design is a cantilever, a single XZ beam with the Y/B rotation on the head. For a more large scale setup we require the X-rail to be attached on two Z rails, like the traditional Prusa-Mendel setup. In order to keep the -90°..+90° Y/B rotation of the head/nozzle, we need to position the rotation center a bit below the X-axis – but . . . this will prevent the nozzle to rotate Y/B to -90° or +90° as the gantry likely will crash with the already printed piece. So, it’s rather a bad idea to do so. As a result, we end up with a limited build volume, as the cantilever will tilt with further extending the X-axis.

Core XB: Combining Linear X with Rotational B (Y-Axis)

That is a very clever setup Joshua did, by having the linear X-axis combined with the rotational B-axis (Y-rotation) which he calls “Core XΘ” (Core X-Theta).

Non-Planar Polar Slicer

Joshua did also code a mixed non-planar slicer which combines multiple schemas as shown in the video:

That is the most tricky part, as polar (flexible tilt) and conical (fixed tilt) slicing may appear sophisticated, but it has its even more expanding challenges as Joshua mentions in his video, and obviously he developed a mixing strategy/schema slicing procedure to slice according the given requirements of printing continuous without supports – as when slicing non-planar, pieces otherwise easy to slice & print, suddenly have overhangs or are non-continuous or not connected anymore. So, he resolved it – let’s see the next weeks – will update this blog-post accordingly.

The S^3 DeformFDM Slicer from Manchester Uni (Tianyu Zhang, Tao Liu, Charlie C.L. Wang) has provided a solution to combine Support Free (SF), Strength Reinforcement (SR) and Surface Quality (SQ) under one umbrella of consideration and optimize all three requirements using quaterions per sample. I tried to adapt that solution but struggled to even build the solution from the sources back in 2022.

Impressive Step Forward

Joshua Bird has “single-handedly pushing the 3DP community forward.” according a YT-comment, and I whole-heartly agree, not only did he thought out of the box with the Core XB setup, but resolved multiple hardware and software challenges – impressive.

References

Misc: Formnext 2024

Updates:

  • 2024/11/26: published
  • 2024/11/23: finishing up, ready for publication
  • 2024/11/21: starting write-up

Introduction

Once again in fall (November 19-22, 2024) Formnext expo opened its doors, and I attended for 2 1/2 days – here my brief write-up and reflection of my experience.

Formlabs

Formlabs made an interesting move: it abandoned the SLA (laser-based) to MSLA (UV light & LCD masking) resin printing with their Form 4 series (Form 4 & Form 4L), so I briefly visited their booth:

I highly recommend the taking apart of the Form 4 by Shane Wighton / Stuff Made Here to get to know all the engineering work which went into the new series.

See more at Formlabs.com

Shenzen Jiexinhua Technology Co. Ltd (SOVOL)

The company behind the commercialization of the VORON series with their SOVOL printers, SOVOL 08 or SV08, a Core XY 350x350x450mm build volume, priced at 550 EUR.

See more at Sovol3d.com

Micro Factory

A small german startup Micro Factory automated the resin printing (incl. washing & curing) within a single case:

Here a brief video of the process (with german commentary in the background):

  1. printing with resin vat
  2. moving plate into isopropyl alcohol (IPA) vat
  3. moving plate to curing position & drying with fans
  4. detaching magnetic plate via electromagnetic release

See more at Micro-Factory.de

So far only Genera‘s G3 does something similar where washing & curing is done in the same case/apparatus.

Prusa Research

Prusa Research announced at the Formnext their new Prusa Core One, a Core XY with 250x220x270mm build volume, nozzle temp. max 300°C, chamber temp. max 55°C, priced at EUR 1,350, with the ability to uprade from MK4S, available later in 2025.

And the official announcement of Prusa Core One:

While I attended the Formnext, Hackaday published a sobering analysis on Prusa’s Open Source stand: With Core ONE, Prusa’s Open Source Hardware Dream Quietly Dies (2024/11/20).

One may argue, Prusa reached a state of commercial success, that it can’t afford to support low-cost chinese replicas of their invention, exactly as it happened to Makerbot and Ultimaker – and both stopped innovate, and recently merged – let’s see if Prusa will thrive in hardware innovation the coming years or lay back as well.

Open Source Software seems more tolerable to commercial pressure: Prusa Slicer still actively developed and forked many times, and so the Cura Slicer by UltiMaker; both slicers enable countless university labs and commercial engineering offices to develop new hardware and having an Open Source implementation of reliable slicers with a ton of detail knowledge embedded when one studies the source code.

See more at Prusa-Research.com

UniFormation

Rather unknown brand to me caught my attention the past year due to high praise from various YouTube reviewers: the older GKTwo (228x128x245mm @30um) ~670 EUR and the new GK3 Ultra (300x160x300mm @20x26um) ~1,200 EUR.

  • integrated heating system at 35°C
  • resin feeding system & resin weight measuring
  • built-in air filter

Industrial-level features while maintaining consumer pricing.

See more at UniFormation3d.com

ApexMaker

The booth featured Pengji (LCD maker) and Apex Maker (MSLA printers) together, so I assumed they are the same business with different brands with LCD making as core business – a representative at the booth confirmed my guess – so aside making UV LCDs they started to produce their own series of MSLA 3D printers:

  • ApexMaker X1: 353x198x400mm @46um pixel resolution
  • ApexMaker X1 Mini: 223x126x210mm @17um
  • ApexMaker X2: 285x214x400mm @25um
  • ApexMaker X2 Mini: 152x87x210mm @17um

Plus their own branded resins, curing and washing stations, see more at Apex-Maker.com

Elegoo

This year Elegoo released Mars 5 Ultra and the Saturn 4 Ultra which have tilting vat alike the Prusa SL1S which decreases layer print time (curing + detaching from film) down to apprx. 5 secs; so I was eyeing to get a Saturn 4 Ultra.

Thursday morning I revisited the booth and asked about whether Saturn 4 Ultra would permit to put a magnet flex plate on it – which would change the Z offset for homing, and two representatives explained to me it would be a bad idea as the home aka zero Z-position would fail, and one would require the change the “G-code” in the firmware (which is closed-source) . . . in other words, the Z offset or home position is somehow hard coded and doesn’t use pressure sensor to calibrate home (this Reddit thread shows Chitubox Slicer allows to alter the homing G-code, LycheeSlicer doesn’t have this option) – the usual rabbit hole.

See more at Elegoo.com

Anycubic

My resin printing farm is composed by Anycubic printers (Mono 4K, X2 and 6Ks), mainly due the diverse third party market for replacement and add-ons, so I was checking out their new Photon Mono M7 Max (298x164x300mm @46um) priced at EUR 900 (2024/11).

I was hoping Anycubic would also implementing tilting vat to decrease the print time and allow more delicate prints due more gentle / gradual detaching from the film with the tilting motion – so far not yet.

See more at Anycubic.com

Fooke & MELD

This basic looking booth I consider a gem among a few others at Formnext 2024, a sample of massive piece which was extruded with aluminium below the melting point using Additive Friction Stir Deposition (AFSD) procedure and yet creating a fully solid piece where layers fully bond – their main photo (1st photo below) or illustration at the booth was deceptive small at first sight, but after looking twice, I realized the entire machine “Fooke AM50” is over ~6m long with a huge build volume of 6 x 3.5 x 1.5m, and aluminium, magnesium, copper, steel and titanium as possible materials, with build rates up to 15kg/h – and all extruded in normal room atmosphere and temperature.

Following video gives an impression of AFSD & FSW based extrusion by MELD (unfortunately the audio is quite silent in most parts):

See more at Fooke Machines.com and MELDManufacturing.com

Spherene

A few weeks I wrote about them already, so I briefly visited their booth as well:

By chance I saw a small table printed with Spherene structure at the 3D Systems booth:

Spherene Side Table
Technology: Stereolithography (SLA)
Material: Accura® AMX Tough FR V0
Software: 3D Sprint®

Long-term environmental stability (tested to 8 years indoor and 1.5 years outdoor mechanical performance per ASTM)

Designed by Peter Donders for:

– Material optimization
– Increased structural strength
– Customization and unique aesthetics

Thermoplastic-like mechanical performance including ductility and elongation at yield

Surface finish comparable to injection molded plastics

And here a brief feature from Formnext 2024 by AM:

See more at Spherene.ch, and also featured in the Joel Telling (3D Printing Nerd) Formnext 2024 stream.

Stratasys

The grumpy grandfather of 3D Printing:

  • grumpy, because of the patent collection which caused the 20 year delay for others to be able to adapt major extrusion technologies, once they expired the “3D printing era” actually started with affordable 3d printers (MakerBot, Ultimaker, Prusa, and all the chinese replicates) and many prosumer derivates
  • grandfather, because it’s truly the beginning of extrusion-based 3D printing / additive manufacturing

See more at Stratasys.com

Chinese Maker Source

A couple of chinese manufacturers providing DIY 3D printer parts:

See more at

WASP

WASP has been around for quite a while with their expertise to extrude clay with their Delta printers but also industrial robots:

See more at 3D Wasp.com

Modix

Modix extended their offers from previous years with primary focus on cost-effective large build volume FDM/FFF printers, e.g up to 2m in Z:

Modix Printers (2024/11)

See more at Modix3d.com

Moxin (Huzhou) Tech. Co. Ltd.

The cylindrical shape of a 3D printer caught my attention, and on the 2nd sight I saw it was a Positron-like setup of printing upside down, the Kokoni Sota printer:

I briefly spoke with Tianrun Chen and he explained when printing upside down, less support material required, and therefore developed their own support generation method which reduces material by 20-50%.

  • 210x200x230mm build volume
  • up to 300°C nozzle temperature
  • optional multi material systems (also in cyndrical case)

See more at Kokoni3d.com

National Taiwan University of Science & Technology (NTUST)

The little booth, and the walls where full of large scale posters with a lot of information, so I stopped and began to absorb the information – in particular the mention of TPMS caught my attention.

So, one of their invention is a resin which when it is baked at 120°C it increases in size XYZ 2x – therefore in volume 8x – which they named “Foaming Resin Printing“, the sample prints like the bike helmet or even the small TPMS Schwarz P cube was incredible light compared to its size.

Another invention is printing TPMS like Schwarz P and then fill the partial closed P cells with a foaming agent, they call this process “Multi-Material Additive Manufacturing using Hybrid 3D Printing and Filling Process” (see non-free paper) – in case of the Schwarz P TPMS the cells are interconnected and spherical, so the agent flows into all captivities quickly as “Liquid Filled Closed Cell Lattice Structures” – and reduces print time while increase stiffness / strength – kind of combining Additive Manufacturing with Mold Injection method.

3D printing closed cells with injection of secondary material

Another achievement is their 32″ (697x392x500mm build volume) and 50″ (1,150x600x500mm build volume) large LCD/MSLA resin printers, with new film coating of better release of the cured layers, although we couldn’t get into the deep details of it. See this non-free paper and this paper for details about the 32″ resin printer.

I could easily spent an hour to explore their different projects and the benefits of those 3 distinct inventions alone. See more at NTUST.edu.tw and its High Speed Printing research center, in particular follow Mayur Prajapati.

TUM (Technical University of Munich)

The TUM had a very prominent and large booth with many departments and many samples shown:

What caught my attention was the “Infinity Node” made from ~1mm gravel, it was printed using SLS-like printing: Particle-Bed 3D Printing by Selective Cement Paste Intrusion (SPI), using concrete as a binder to connect the small gravel together. The “Force-flow Optimized Node” (brown triangular truss shape) was done using Particle-Bed 3D printing by Selective Cement Activation (SCA).

The other samples were “Molten Metal Jetting” (Copper), “Functional Graded Material” using Plasma Directed Energy Deposition with Powder Feedstock, and “Laser Cladded TUM Logo” using Laser Metal Deposition (316L + Construction Steel).

See more at TUM Additive

Quantica

Quantica caught my attention 2-3 years ago, also at the Formnext, as I saw the modular fully voxel-exact jetting of material. Their core business was their printhead “NovoJetTM” able to print high viscous materials, something other jetting printheads would not able to do.

This year I talked to Sven, and he explained to me that they broaden their application areas like exact glue jetting and other high viscous materials, as some industries saw their printheads as high precision material deposition, outside of the “3D printing / Additive Manufacturing” use cases – quite interesting how their core expertise found other applications.

Again I asked for some 3D printed samples, and again I was denied any like the past 2 years (!!) – they said they don’t have any (or very few) but just for internal use; my main interest is to observe the actual blend or non-blend and then 3D dithering of materials and colors on drop level in order to explore new applications.

See more at Quantica.io

Xolo with Xube2 & Xell Printer

An interesting approach, new to me, is Xolography, spatial printing in a resin – first a light sheet is established in the Z plane of 50um thickness, and then XY image is projected into the volume, different color/wavelength for each, and where the Z plane and the XY image light meet, there the photopolymerization takes place; it only works with highly transparent resins, more precisely dual-color photoinitiator (DCPIs), as otherwise the Z plane won’t reach all XY 2D image.

Xolography: UV light Z plane intersecting with XY plane with visible light
  • first wavelength (375 or 405 nm, UV light) activates the dormant initiator by triggering a spiropyran photoswitch (Z plane)
  • second visible wavelength (450-700 nm, visible light) excites the benzophenone component to initiate polymerization (XY plane)

So far they reached 50x89mm XY size, yet any size in Z including continuous Z printing.

They claim “no layer lines”, and told me they project a “video”, yet, a video has also n-amount of images e.g. a certain frame rate. What they rather mean is a continuous Z motion while they project their Z sliced XY images, and because the Z motion is continuous, the “layers” rather blend together and get smoothed out. They also don’t need any support structures, as the cured/solidified structure stays in-place and doesn’t sink.

Their requirement of highly transparent resin they derive one of their main use cases: 3D printed optics, and isotropic properties (angle independent strength, no deliminations), other resins they developed are bio-compatible and even soft and gel-like – quite an impressive achievement.

Currently they have two machines available:

  • Xube2: 10x18mm (@5um feature resolution) up to 50x89mm (@25um feature resolution) x80mm, apprx. 6mm/min in Z, feature size 5-25um as mentioned, although 50um lightsheet width/thickness, so the “feature size” more applies to XY than Z
  • Xell: 10x17x10mm build volume, @10um feature size

This process has been new to me as mentioned, but while researching I realized it has been around since 2020 or so, when this paper was published.

See more at Xolo3d.com; see also a section of Joel Telling (3D Printing Nerd) Formnext 2024 stream.

Asahi Kasei

They showed a bio-attributed Cellulose Nano Fiber (CNF) made from cotton fibres, and then reinforced polyamide PA66 into a filament named CNF/ECONYL® Polymer with extended heat resistance, strength (performing better than carbon fiber), smoothness and formability – actual details of measurements are missing to backup those claims – according their announcement the material becomes available in Q3 2025.

While their booth has been very small and unassuming, the company has nearly 50,000 employees world-wide.

See more at Asahi KASEI and especially this announcement.

Genera

As far I saw they are one of few which makes resin printers which print, wash and cure in the same machine (Genera G3), where as their G1+F1 and G2+F2 they call “glove free process with shuttle technology and automated post-processing”, where the build plate is carried over in an encasing “glove free” to wash and cure station (F1 respectively F2).

  • Genera G1 + F1: 134 x 76 x 140mm build volume @70um pixel resolution
  • Genera G2 + F2: same as G1 plus automatic platform change for queued printing
    • 153-384 x 87-216 x 320mm build volume (@40, 70 or 100um pixel resolution)
  • Genera G3: same as G2 but has F2 integrated, print, wash & cure in one machine
    • 268-384 x 153-216 x 320mm build volume (@70, 100um pixel resolution)
Genera G3 (3D Model): (left-to-right) resin vat, washing vat, and curing vat

See more at Genera3d.com

Meltio

Printing metal poses the biggest challenge in my eyes as you have to put immense amount of energy to deform metal, while maintain or desire high degree of precision. So Meltio focuses on Directed Energy Deposition (DED) and Wire-Laser Metal Deposition (W-LMD): stainless steel, carbon steel, titanium alloys, nickel alloys, copper and aluminium:

Their sample prints are massive, and quite rough compared (0.8-1.2mm wire diameter) to Selective Laser Melting (SLM, powder-based) models where we get 50um feature size.

Their “Engine Blue Integration Kit” integrates into existing CNC machines making it hybrid operation and achieve traditional CNC tolerances and feature size – one of the many examples where Additive & Subtractive Manufacturing are put together with their respective strengths.

See more at Meltio3d.com

Fraunhofer

Fraunhofer is a prominent research institute in Germany, with multiple booths displaying cutting edge AM methods – I didn’t have the time to dig deeper into their samples, somehow nothing stood out prominently, or it was vaguely explained at first sight unlike at other booths.

See more at Fraunhofer.de

Markforged

That booth was crowded most of the times, so I did only pass by – so far the FX10 caught briefly my attention:

  • 375x300x300mm build volume
  • FDM/FFF operating with
    • Metal: Metal Fused Filament (MFFF) with sintering post-processing, or
    • Composites: other composites incl. Continuous Carbon Fiber (CCF)

See more at Markforged.com

DMG Mori

Whenever I pass by their booth, the only thing I think is “car-sized 3D printers costing millions of EUR/USD”, it’s a different use-case from where I operate – as simple as that, and yet, still impressive what they achieved.

Btw, its name comes from multiple mergers: DMG (Deckel, Maho, Gildemeister) and Japan’s Mori Seiki – a blend of german and japanese workmanship.

See more at DMG MORI

Mosaic

A few years back in 2017 they released their Mosaic Palette, splicing (cutting and melding together) filaments ahead for a single filament yet achieving multi-color with a single nozzle without material waste. Meanwhile they massively scaled up their expertise and developed their own printers called Mosaic Element and print farm called Mosaic Array – and I admired the overall case design in simplicity how the filament cartridges are attached.

Element HT2 specs:

  • nozzle up to 500°C
  • build plate up to 120°C
  • build chamber up to 80°C
  • 350x350x350mm build volume
  • up to 8 materials via filament cartridges
  • price starting at EUR 9,500

Their print farm is built on stacked printers, and a robot arm removing the built-plate with the printed piece and stack it below and refill the printers with empty build plates again.

See more at MosaicMFG.com

Miscellaneous

3D Printing Nerd

While roaming in the halls, I recognized a few YouTubers like Ross Graham (FauxHammer), Jon Schone (Proper Printing) and also Joel Telling (3D Printing Nerd):

Here his marathon live-stream at Formnext 2024:

Among others were also Xolo with the Xolography light printers, Spherene with Daniel Bachmann, Josef Prusa showing the Prusa Core One, and Stratasys representative featuring SAF H350 able to process used powder with High-Energy Absorption Fluid (HAF) with Selective Absorption Fusion (SAF).

Tony Lock (Duet) & Jon Schone (Proper Printing)

As I was visiting the booth of OST (Eastern Switzerland University of Applied Sciences) & Spherene I met Jon Schone (Proper Printing) and Tony Lock (Duet3D) and we had a brief talk on multi-axis firmware & slicing and parallel printing as Jon showed in his video with a multi-gantry setup – I’m currently composing/writing on a blog-post about parallel/concurrent printing with multiple nozzles so I was keen to briefly exchange thoughts on this topic.

Tony Lock (Duet3D) & Jon Schone (Proper Printing) – Formnext 2024

Reflection

Formnext is an overwhelming expo on all things on Additive Manufacturing, and this year less people and less exhibitors than last year. Also a few startups with big booths last year either had a small one (nTop, Inkbit, etc) or were no longer present. A couple of newcomers and startups, and some companies I have been following pushing their innovation further and exploring new applications, like Quantica.

I only stayed for 2 1/2 days (Tue-Thu) instead the full 4 days, as from last year I knew I wouldn’t be able to absorb more things with more time, there was only so much to take in. For me it was worth it, to reconnect with companies and individuals I know only online and via video calls.

The “3D Printing Hype” has peaked, the stock prices (state 2024/11/22) of Stratasys (8% or -92% of max), 3D Systems (8% or -92% of max), Desktop Metal (1.6% or -98.4% of max) or Nano Dimension (15% or -75% of max) have shown how much air and speculation was there, and now the sobering realism and use case are established. The same time MakerBot & Ultimaker merged, two exhausted 3D printer manufacturer without hardware innovation in the past years – yet Prusa, Creality, Elegoo, Anycubic still thrive on the pro- and consumer level with both FDM/FFF and MSLA resin printers, and BambuLab so successful that it caught Stratasys’s attention to sue them about patent infringements.

It’s clear to me, that Additive Manufacturing (AM) has its unique use-cases, whereas injection molds still dominate mass production due the high volume capacities and cost effective production. Additive Manufacturing may be a bland term (I like “3d printing” better), but there are still many possibilities to be explored, as Xolo or diverse university booths have shown – any kind of granular or liquid material is melted, extruded, jetted, cured or bound with an agent.

Hammer Man in Frankfurt/Main (Germany) – Symbol of a Maker

References

That’s it – see you perhaps in 2025 there.

3D Design: Spherenes

Updates:

  • 2024/10/23: finally published
  • 2024/10/11: ready for publishing
  • 2024/01/26: starting write-up

Introduction

At Formnext 2023 I spent some unexpected time to discover a new class of procedural structure called “Spherene” (“sphere” + “graphene”), it’s a name as introduced by a company with the same name.

It’s main feature is isotropic (“all directions”) distribution of forces. Their service provides the creation of this structure based on:

  • density (ratio of material vs empty space), hence their term of Adaptive Density Minimal Surface (ADMS)
  • form
  • wall thickness

where all of them are freely definable in 3D space contained within an overall boundary. Their service “renders” a mesh which complies with such, like defining at some point a lower or higher density, and transits in 3D space from one to another.

Spherene Metamaterial in Simulation-Based DFAM: CDFAM NYC 2024 (Video Presentation)

Patent

My immediate impulse was to code the Spherene aside of the existing TPMS’s, but I realized their business core is the service of creating meshes based on their procedure as described in a patent:

  • Method of Additively Manufacturing a Minimal Surface Structure (Original, 2023), PDF available
  • at its core it describes 6 steps (abbreviations added for clarity)
    1. creating envelope
    2. creating density field
    3. adaptive Voronoi tesselation (AVT),
    4. 1st skeleton graph (SG) associated to AVT (SG-AVT1)
      • generated from the edges of the Voronoi cells
    5. 2nd skeleton graph associated to SG-AVT1 (SG-AVT2)
      • generated using Delaunay tetrahedralization
    6. minimal surface from SG-AVT1 and SG-AVT2, using equidistant from both skeleton graphs, with minimal wall thickness requirements
  • Abstract: A method of additively manufacturing a minimal surface structure of a three-dimensional article includes a computer executing the steps of recording, in the computer,
    • an envelope of the three-dimensional article; generating a density field across a volume enclosed by the envelope with densities of the density field corresponding to local requirement values of at least one physical parameter at respective positions of the three-dimensional article;
    • generating an adaptive Voronoi tessellation of the volume using the density field;
    • generating a first skeleton graph associated with the adaptive Voronoi tessellation;
    • generating a second skeleton graph associated with the first skeleton graph; and
    • generating a digital minimal surface model from the first and second skeleton graphs.
  • The method may further include a 3D printer additively manufacturing the minimal surface structure according to the digital minimal surface model.

I think if Spherene is truly as significant for Additive Manufacturing, and an essential invention, it has to move beyond the grip of a single company and its patents – time will tell.

Samples

Daniel Bachmann from Spherene Inc. kindly shared with me a few samples, 20x20x20mm cubes, and 20mm diameter spheres with Spherene infills, illustrating their properties:

A few support structures were required for the spherical samples, the cubic samples did not require such:

Lychee Slicer: spherical spherenes required support structure due overhangs

Additionally I printed a few cubic samples with FDM on my CoreXY Ashtar C without supports at 40x40x40mm scale.

Subtractive Manufacturing & Molding Usage

The structure cannot very well machined with subtractive manufacturing processes – or only if the piece is sub-divided so all indentations can be milled, and sequentially fused or welded again.

Another approach comes to my mind is to form dedicated bricks, e.g. for large scale application like a building, and have a limited kinds of bricks depending on their position and use case, and have molds to form those limited kinds in larger quantities.

In order to produce a mold one would inverse the original model, the negative volume, that would be produced using additive manufacturing and then produce lost-form casting molds, or highly simplify the form so one can remove the positive without destroying the mold.

References

3D Design: Moebius Strips

Updates:

  • 2024/10/20: published (back dated)
  • 2024/10/11: ready to publish finally
  • 2024/01/16: starting write-up

Introduction

Mathematician August Ferdinand Möbius described in his paper 1858 a shape, which later became known as “Moebius Strip”: a surface with only one side or surface and one edge:

In the physical one can take a strip and twist it once and tape both ends – when creating a 3D representation, one rotates a strip by 180° to a closing circle.

Examples

3D Prints

I printed some of the models on MSLA resin printers with plenty of supports, one more flat and another series more vertical oriented which required more support structure and harder to remove.

References

3D Design: 3D Lissajous

Updates:

  • 2024/01/22: published without much reflection & conclusion as research is ongoing
  • 2023/12/02: adding more examples and refining details
  • 2023/10/22: start writeup

Introduction

While studying continuous fiber 3D printing and its main nature is to find ways to lay fiber without interruption. In order to refresh my memory I revisited the Lissajous forms, which until recently only knew in their 2D form, the swirling strings or lines – and now extending it into 3D as well.

The main idea is to realize how a line, string or fiber can be used to fill non-planar and circumvent a 3D structure and how angular shifting in Lissajous context affects such form.

3D Lissajous

  • angle: 0 .. 2pi or 0 .. 360°
  • p, n, m: 0 .. 1000, the amount of loops
  • phi0, phi1, phi2: the angular offsets 0 … 2pi or 0 .. 360°
  • X = sin(angle*p+phi0)*r
  • Y = sin(angle*n+phi1)*r
  • Z = sin(angle*m+phi2)*r

I did a lot of experimenting – I could post hundreds of forms – but let me focus on one a bit closer, which got my attention:

It is a very interesting transition, 8/13/21 with phi0=0° gives almost a cube-like structure, and shifting the X loop to 90° we get a tetrahedron:

Spherical Lissajous

While playing with 3D Lissajous, I thought to adapt the cyclic nature, but apply it to a circle laying in the XY plane and then rotate in X axis, and Y axis as well, and optionally cyclic translation as well:

  • d: diameter
  • angle: 0 .. 2pi or 0 .. 360°
  • p: amount of loops as in X=sin(angle*p)*d/2, Y=cos(angle*p)*d/2
  • q: amount of X rotations: rotateX(angle*q)
  • r: amount of Y rotations: rotateY(angle*r)
Spherical Lissajous 12.23 with spreading struts

The model was printed with MSLA white resin at XYZ 50um resolution with 120mm diameter, with a few support structures near the bottom:

Spherical Lissajous with Translations

Using the Spherical Lissajous and extend it slightly:

  • [A,B,C]loop/offset/radius: translate([ sin(angle*AL+AO)*AR], sin(angle*BL+BO)*BR], sin(angle*CL+CO)*CR ])

which spreads the ribbons away from the spherical surface origins.

Spherical Lissajous 5.11 AL=3, AR=5

Spherical Lissajous 5.11 AL=3, AR=5

It’s symmetric X- and Z-wise, in Y-axis it isn’t.

The model was printed with MSLA white resin at XY 35um / Z 50um, at 60mm in Z height, ~94mm width; with a some support structures:

Spherical Lissajous 11.15 AL=2, AR=5

A more elaborate form is 11.15 AL=2, AR=5:

Spherical Lissajous 11.15 AL=2, AR=5 with spreading struts

So, there is no X-, Y- or Z-wise symmetry.

The model was printed with MSLA white resin at XY 35um / Z 50um, at 60mm in Z height, ~94mm width; with a some support structures:

and printing it larger with ~200mm width with manually positioned support:

That’s it (for now).

References

MSLA Anycubic Photon Mono X 6Ks

Updates:

  • 2024/11/30: Mono X 6Ks reversed engineered (UV LCD, GUI LCD)
  • 2023/11/18: published
  • 2023/11/11: printer ordered
  • 2023/10/24: starting writeup

Introduction

Anycubic Photon Mono X 6Ks

After using Anycubic Photon Mono X2, and not that happy with it, I saw Anycubic released another resin printer:

  • build volume: 195.8 x 122.4 x 200 mm (WxDxH), same size as Mono X2:
    • reuse all its third party parts also compatible with X2: vat, FEP films etc
  • resolution: XY 34μm, Z 10-50μm
  • 9.1″ display with 6K resolution (5760×3600) display
  • monochromatic LCD (hence “Mono”), faster printer due shorter exposure
  • affordable with EUR 250-280 (2023/11)
  • no network, only USB drive printing

The Photon Mono X2 with XY 48μm has not so well performed for me in regards of precise parts, as 100μm precision was not really delivered, either it was underexposing and fail on 1mm walls, or solid walls but thicker – somehow I could not find a good balance. The Photon Mono 4K performed more reliable for me and was able to print geometrically more reliable and correct with XY 35μm; so my hope is that the X 6Ks delivers with 34μm pixel size.

I ordered the Photon Mono X 6Ks in 2023/11 for EUR ~230 incl. shipment, and it arrived a few days later from a warehouse in Germany to Switzerland.

Anycubic’s Naming Convention & Communication

The X 6Ks is kind of successor of the X2 with the same body shape and build volume, but who cares of good naming? It could have been X2 6K simply. Aside, Anycubic seems the care little to provide customers the XY resolution anymore on info material or their web-site (2023/11) but only state XY build area and XY resolution, and announces 4K, 6K or whatever – you need to calculate the XY pixel size yourself . . .

Print Settings

retrieved from Anycubic 2023/11/11 as screenshot:

For Lychee Slicer (2023/11) I recommend following settings:

  • enable Two Stage Motion Control (TSMC)
  • Bottom Layer: 1-6 (1 for small pieces, 6 for larger pieces)
  • Transition Layers: 10 (for flat pieces reduce to 1)
  • Lift Distances: [1] 4mm, [2] 8mm
  • Lift Speeds: [1] 1mm/s, [2] 3mm/s or 60mm/min, 180mm/min

Here my settings:

Flex Build Plate

I’ve got a spring steel plate 202×128 mm from Aliexpress, and as I ordered two of them for X2 but I can reuse one for the X 6Ks as well:

  • magnetic base: 2.2mm thick, slightly extends, 203x129mm
  • steel plate: 0.5mm thick, exact 202x128mm, I roughed up the exposed surface with sandpaper to increase adhesion

so one has to compensate 2.5 – 3mm in Z height and adjust, speak extend, the Z-level probe. I just glued a black strip of paper, and then of course re-level the bed again afterwards.

Glued a piece of paper extending it ~2.5mm (I used black marker to darken the paper afterwards)

Update 2024/11: I had mixed long-term experience with flex steel plate with X2 (same build plate size as X 6Ks).

Preliminary Review Mono X 6Ks

Pros:

  • cost-effective for 196x122mm build area and 34μm pixel size
  • good third party market (X K6s parts are mostly compatible with X2, except UV LCD & mainboard)

Cons:

  • Mechanical Quality Control (QC):
    • build-plate has too much horizontal play (2+mm), the Mono X2 build-plate which has the same size fits tighter (see below for detailed photos)
    • grey/clear upper case is/was warped, doesn’t fit seamless off by 3-4mm bent inside on left & right; likely package long stored in wrong position or bad clear plastic deforming over time (remedy below “Cover Wedges”)
  • no WiFi
  • very basic firmware
  • poor quality SD stick, it usually won’t last a couple of weeks before it becomes unwritable or unreadable, but this time I couldn’t even backup the data, the stick failed right away; visit anycubic.com and download the slicer, handbook and test files to calibrate UV exposure times

Anycubic is known to invest very little into the firmware which is provided by Chitubox, given they are one of the biggest resin printer sellers, it affects 100,000s of customers.

The build plate also has new a black plastic connector to the mount, an attempt to reduce cost perhaps. The Mono X2 build plate mount is much better in my opinion, more solid; the Mono X 6Ks build plate has more wiggle/play horizontally, therefore pieces printed you want definitely in the center of the UV LCD/vat, not on the side to avoid any wiggle or play while printing introducing imprecisions.

Cover Wedges

As the X K6s came with a warped cover, and with the X2 with the same case geometry it was wiggly everytime I put the cover over, I printed a few “wedges” which align the cover easily:

and I did the same with my Mono X2.

Reverse Engineering

Goran Mahovlic started to reverse engineer the Mono X 6Ks:

See also the Github repo reverse Mono X 6Ks; the UV LCD and the user GUI LCD are working.

  • GD34F427 MCU (Cortex M4, 512KB Flash, 96KB RAM)
  • 32MB SDRAM
  • SPI 16MB Flash
  • FPGA 5K LUTs
  • DRV 8424 stepper motor driver

(M)SLA Value Comparison

A numeric value summarization for features I care about, the rough & simple formula:

XY Area [mm2] / Price [EUR] / XY Resolution [μm]

The bigger the XY area, the smaller the price and the smaller the resolution, the higher the value:

State 2024/11

PRINTERyearXY RESOLUTIONTypeBUILD VOLUME XYZPRICEValue
Elegoo Saturn 42024-19μm x 24μm2)MSLA218x122x220300 EUR3.74
Elegoo Saturn 4 Ultra2024-19μm x 24μm2)MSLA218x122x220400 EUR2.81
Anycubic Photon Mono M52023-19μm x 24μm2)MSLA218x123x200410 EUR2.72
Elegoo Mars 5 Ultra2024-18μmMSLA153x78x165270 EUR2.54
Anycubic Photon Mono X 6Ks2023-34μmMSLA196x122x200280 EUR2.51
Anycubic Photon Mono M72024-17μm x 25μm2)MSLA223x126x230450 EUR2.49
Elegoo Saturn 3 Ultra2023-19μm x 24μm2)MSLA218x123x260480 EUR2.32
Anycubic Photon Mono 4K2021-202235μmMSLA132x80x165150 EUR2.01
Anycubic Photon Mono X22023-48μmMSLA196x122x200260 EUR1.91
Anycubic Photon Mono 22023-35μmMSLA143x89x165210 EUR1.73
Elegoo Jupiter SE2023-51μmMSLA277x156x300700 EUR1.21
Anycubic Photon M7 Max2025-46μmMSLA298x164x300900 EUR1.18
EMake Galaxy 1202325μm / 100μm1)SLA400x200x4002,700 EUR1.18
Anycubic Photon M3 Max2022-202445μmMSLA298x164x300980 EUR1.10
UltraCraft Reflex RS2024-30μmMSLA222x122x2001,200 EUR0.75
EMake LCD 16″ 8K2024-46μmMSLA353x198x4002,700 EUR0.56
Formlabs 3L2020-202425μm / 100μm1)SLA335x200x3009,000 EUR0.29
Formlabs 3+2020-202425μm / 100μm1)SLA146x145x1853,500 EUR0.24
Formlabs 4L2024-46μmMSLA353x196x35011,000 EUR0.14
Prusa SL1S2021-49μmMSLA127x80x1801,700 EUR0.12
Formlabs 42024-50μmMSLA200x125x2105,175 EUR0.09
  1. Formlabs 3’s & EMake SLA printers use a laser beam which has 100μm in diameter, but it can be positioned 25μm exact, the latter was used to calculate the value
  2. The UV pixels are non-square, the longer side was used to calculate the value

The XY area in SLA scales not as good as with MSLA, as the laser beam takes longer the more XY area (e.g. more pieces) need to be rendered per layer – so, MSLA is recommended for aiming fast parallel printing. Interestingly Formlabs 4 is now a MSLA as well (2024/04), they seem to have abandoned the SLA laser-based approach.

Anycubic Mono X 6Ks, X2 and 2x 4K

One of the main reason I choose Anycubic MSLA is the third party market for add-on’s, like flex build plate, vats, LCD replacement etc, and I also like the build plate mount with 4 screws for alignment; and I hoped to replace the firmware of the Mono 4K which did not happen as the Open Source variant is still incomplete (2023/11).

References

Misc: Formnext 2023

Updates:

  • 2023/11/13: published
  • 2023/11/11: starting writeup

Introduction

Another year, another November in Frankfurt (Germany) and Formnext – this is the main event of the year professionally for me. As I reside in Switzerland the travel is fairly easy and short and the 770 exhibitors in two halls (11 & 12) with two floors each is so overwhelming that even 4 days attending is not sufficient.

  • Day 1 (Tue, Nov 7): I spent an entire day to explore hall 12.1 alone, which turns out a good choice as it was a dense populated hall with many smaller companies
  • Day 2 (Wed, Nov 8): visiting with a client half of the day to review some of their possible competition, and then explore 12.0
  • Day 3 (Thu, Nov 9): some schedules meetings and then explore 11.0 and 11.1
  • Day 4 (Fri, Nov 10): revisiting 12.1 and 11.1 briefly, visiting with another client some selected booths to check products on display

I surely missed a few booths in 11.1 and 12.1 still; whereas 12.0 and 11.0 were more large scale industrial AM solutions, mixed with university and regional focused booths which I didn’t have time to explore in detail.

Personal Selection

I feature some companies according my personal professional interests:

Spherene (Math)

I made contact with Spherene before via LinkedIn but I realized I missed the point of what Spherene actually “invented”, at their booth Daniel Bachmann took the time to show me the features of their new class of minimal surface model and it was challenging for me to follow him despite of my own experience with Triply Periodic Minimal Surfaces (TPMS) – after apprx. 20 mins I realized the scope and some of their depth of their “invention”.

In essence, the sphere is used as a base form, and density, wall thickness and other features are processed in a localized manner, filling the space. The main result doing is optimizing a form to distribute inner/outer forces, e.g. the ends of the spheres are perpendicular to the surface providing ideal way to distribute them into a network of thin walled interconnected spheres providing isotropic (“all directions”) property.

The samples on display were printed with MSLA, SLA, FFF/FDM or SLM were indeed very strong in relation to the printed volume, e.g. the hallow rabbit printed with resin barely gave in when pushing on the thin outer perimeter – impressive.

Their approach is available as cloud-based GUI or as Grasshopper/Rhino plugin. The actual details of their procedure isn’t easily found but a patent (WO2020229692A1) by CEO Christian Waldvogel gives some idea.

Genera (DLP Resin)

There are many MSLA/SLA/DLP printer manufacturers, yet, I wasn’t aware of Genera and I was shown their system, an integrated workflow:

  • all resin vats have a lid (only applies for G1/F1 combo but not their bigger machines), which are opened only within the machine
  • the finished prints (still on the plate) are moved in a box into the washing machine (without any person touching resin or the resin coated prints)
  • once automatically cleaned and post-cured, the prints are removed from the build plate manually

In essence one does not interact with resin directly, it’s all contained within the workflow – which I like a lot. They also provide wide selection of resins: hard, soft, rubbery, opaque, transparent/clear.

My idea has been to adapt some of their approach to make my own resin printing with Photon series (4K, X2 and X 6Ks); right now I also have multiple vats, and flex-plate, but moving the printed parts and washing them are still messy.

Quantica (Resin Jetting)

Last year I already visited the booth of Quantica, and so this year again. I asked earlier for printed samples, but they declined, and again this time . . . it is bizarre to see a machine actually able to print, and they don’t hand out samples, but I was told by January 2024 I might get some. This tells me a few things, the printed pieces are very sparse or not yet at the quality they want others to experience – some samples were on display, but sealed behind a glass box unable to have in my hand. So I guess now, they are expecting or already have better and more reliable printing results where the printed pieces match other similar printing processes.

I follow their development closely since ~2 years as I consider it very innovative to print with 7 different resin-based materials at the same time and able to fine-tune material properties on the voxel-level.

Duet3D (Open Source Hardware & Community Building)

UK-based Duet3D with its Duet boards and RepRapFirmware (RRF) is, as I wrote before, a beacon within the Open Source Hardware community – it isn’t just an example for other companies, and but also a great synergy provider, aiming to bring different individuals, groups and companies together.

Brandon Builds’ Open 5X version was featured on a Voron 0, and a second machine also 5-axis setup with a tool changer.

Rapid Liquid Printing / RLP (Flexible Structures)

While roaming around a small booth of RLP caught also my attention, where a video was featured of a nozzle moving in a bed filled with silicon printing rubber, and other flexible material:

Reinforce 3D (Enhancement)

Another truly innovative approach combining and enhancing existing additive manufacturing processes was shown by Reinforce 3D:

  • using existing AM methods such as SLM, SLA, MSLA and even FFF/FDM to make models with thin walled tunnels and then
  • filling or rather pushing them with strains of carbon fibres along with resin into the tunnels
  • and thereby reinforcing free forms by keeping the result lightweight but incredible strong due the embedded carbon fibres

A very small but significant detail is, that you can print multiple parts on a smaller printer, but once you start to insert the bundles of carbon fiber those segments of pieces get combined in a strong assembly, as the aluminium skeleton shown above.

Plasmics (Inductive Heated Hotend)

INo Trident – inductive hotend by Plasmics: fast heatup and cooldown / 3s from 20C to 220C, 10s from 220C to 150C

At the booth of Plasmics I looked at the inductive hotend and saw the heating up in a few seconds from 20C to 220C and cool-off in a small demo first hand.

The hot part of the nozzle looks like a needle, with little thermal mass, hence the fast heat and cooling-off time, and then surrounded by ceramics with the inductive coil on it.

The hotend incl. the controller board priced at EUR 400 is high for DIY enthusiasts but low for an industrial setup.

Miscellaneous

Major AM players were present:

  • Formlabs: industrial SLA & SLS
  • Markforged: 3-axis Continueous Carbon Fiber (CFF)
  • Nexa3D: industrial SLA & SLS
  • Prusa Research: the usual lower-end/lower cost printer and their industrial aimed printers of the “Pro” series
  • Elegoo: low-cost resin & FFF/FDM printers, resins & filaments
  • Anycubic: low-cost resin & FFF/FDM printers, resins & filaments
  • BambuLab: cost-effective quality high-speed FFF/FDM printers
  • Creality: low-cost FFF/FDM & resin printers
  • Modix: low-cost but large scale FFF/FDM printers
  • Polymaker: filaments
  • eSun: filaments & resins
  • many smaller filament seller
  • E3D: hotends, extruders

And UltiMaker (after Ultimaker & MakerBot merger) wasn’t present again; the consensus has been that BambuLab‘s printers have taken the higher quality consumer FFF/FDM printers market segment, and the air getting thinner for UltiMaker – at the same time they are doing a great service with the Open Source Cura slicer.

Random Impressions

References

IoT: Milk-V Duo (RISC-V) eSBC running Linux

Updates:

  • 2024/02/18: added AlpineLinux for Milk-V Duo 256M
  • 2024/02/15: adding Ubuntu-22.04 & ArchLinux disk images for Milk-V Duo 256M version, and new photos of 256M version as well
  • 2023/11/20: Ubuntu 22.04 v0.0.2/0.0.3 images released
  • 2023/11/02: ArchLinux 2023-10-09 v0.0.3 image released
  • 2023/10/31: distro update on Debian RISCV64, Fedora 38, Alpine Linux; description how to set static IP for host when using RNDIS
  • 2023/10/21: released ArchLinux disk image with RNDIS support
  • 2023/10/16: creating custom disk images, releasing my own disk image tagged “spiritdude”
  • 2023/10/11: 2nd option for swap space, OLED SSD1306 example with TinyCC
  • 2023/10/10: adding Resizing Disk, Start Script at Boot, TinyCC, GPIO, Pinpong, Software State update
  • 2023/10/05: published
  • 2023/10/03: adding printable case and guide to add multiple boards on a host
  • 2023/09/21: starting writeup

Introduction

In 2023/09 I purchased a couple of Milk-V Duo boards at USD 5.00 / piece, which supposed are able to boot and run Linux – a very competitive option.

Milk-V Duo board

Within 3D printing context it can or could serve as:

  • real-time motor controller
  • tiny AI component to detect failed prints via camera (check these threads on ‘yolo’ and MilkV Duo: TPU)
  • running Klipper on it, likely requires expansion board with additional USB ports, not yet tested; ArchLinux repo provides it though
  • any kind of quick experimental setup when Linux is a requirement, and Raspberry Pi perhaps overkill already, and ESP32 not powerful enough to run Linux*)

*) as of 2023/09 a few people working on Linux for ESP32-S3

A couple of years ago I used ESP8266 with Lua, and ESP32 with Lua and MicroPython, but the functionality was very limited, although it had WiFi built-in, but barely ran anything more complex or serious – the Milk-V Duo changes this, at the same pricing of USD 5.00-9.00; and I really looking forward to have just a small device running Linux, and competing with Raspberry Pi’s which either hardly were available or sold at 2-3x the announced price.

and the newer 256MB RAM variant (released 2023/12):

Specification & Features have been partially copied from Milkv.io (2023/09 & 2024/02):

Specification

Milkv-DUO (64M)MILKV-DUO 256M 1)MILKV-DUO 512M 2)
ProcessorCVITEK CV1800B (C906@1GHz + C906@700MHz) + 8051@8KB SRAM + TPUSG 2002 (C906@1GHz + C906@700MHz) + (Cortex-A53@1GHz) + 8051@6KB SRAM + TPUSG 2000
MemoryDDR2 64MB DDR2 256MBDDR2 512MB
Storage1x Mirco SD slot
1x SD NAND solder pad
1x Mirco SD slot
1x SD NAND solder pad
USB1x USB-C for data and power
1x USB2 solder pad
1x USB-C for data and power
Camera1x 16P FPC connector (MIPI CSI 2-lane)1x 16P FPC connector (MIPI CSI 2-lane)
GPIOup to 26 Pins available for general purpose I/O(GPIO)up to 26 Pins available for general purpose I/O(GPIO)
Size21mm * 51mm (same as Raspberry Pi Pico)21mm * 51mm (same as Raspberry Pi Pico)
PriceEUR 5.00-7.00EUR 7.00-9.00
  1. available since 2024/01
  2. not yet announced

Features

Processor

  • 1GHz and 700MHz RISC-V C906 processors
  • 8051with 8/6KB SRAM
  • Integrated CVITEK TPU for smart detection
  • Supports H.264/H.265 video encoding, up to 2880×1620@20fps
  • Compatible with high-definition CMOS sensors
  • Programmable frequency output for sensor clock
  • Comprehensive ISP features for image optimization
  • Partial OpenCV library support with CV hardware acceleration
  • 16-bit audio codec with built-in mic input and output functions
  • Flexible network configurations with 1 Ethernet PHY

CSI-2 (MIPI serial camera)

  • Features a 16-pin FPC interface for 2-lane MIPI camera input
  • Operates I2C, CLK, and RST signals at a 1.8V voltage level

Ethernet

  • Milk-V Duo includes CV1800B chip with a 100Mbps PHY
  • PHY is linked to a 5-pin solder pad
  • External transformer and RJ45 socket are needed for Ethernet use

USB

  • USB 2.0 compliant, backward compatible with USB 1.1
  • Supports various speed modes, Host/Device functionality, and transfer protocols
  • Expandable interfaces via USB Hub (up to 127 devices)
  • Power-saving mode, supports HID devices
  • Functions as USB slave device with configurable software
  • USB Type-C for storage media access

Micro SD

  • SDIO0 is compatible with Secure Digital Memory (SD 3.0) protocol

No WiFi (but Ethernet over USB)

  • it has no WiFi (either use ESP8266 or ESP32 at WiFi gateway via UART, but this kind contradicts the entire single board concept)
  • USB(-C) connector can be used in double feature mode: as power supply and pseudo network called RNDIS (virtual Ethernet over USB)

No HDMI / Video Out

  • it has no video out, at best I2C or SPI-based LCD can be connected, and function as framebuffer (see this post as a start)

Pinout

  • Up to 26 GPIO pins on the MilkV-Duo 40-pin header provide access to internal peripherals such as SDIO, I2C, PWM, SPI, J-TAG, and UART
  • Up to 3x I2C
  • Up to 5x UART
  • Up to 1x SDIO1
  • Up to 1x SPI
  • Up to 2x ADC
  • Up to 7x PWM
  • Up to 1x RUN
  • Up to 1x JTAG

Distributions for Milk-V Duo 256M

Status 2024/02

DISTRIBUTIONMaturityCapabilityAdvantagesdisadvantages
Ubuntu 22.04 RISCV64 ★★☆☆☆★★★☆☆– rndis / usb network
– package management (apt) works with 240MB available RAM
– rootfs 8GB, 600MB used
ArchLinux RISCV64★★☆☆☆★★★☆☆– package management (pacman)
– rootfs 8GB, 960MB used
– rndis / usb network
AlpineLinux RISCV64★★☆☆☆★★★☆☆– package management (apk)
– rndis / usb network (but random MAC addresses)
– rootfs 1GB, 150MB used

Distributions for Milk-V Duo (64MB RAM)

Status 2023/11

DistributionMaturityCapabilityAdvantagesdisadvantages
Duo BuildRoot SDK★★★☆☆★★☆☆☆– works
– compact
– rndis / usb network
– cumbersome bootstrap (make menuconfig)
– no easy install of apps on live system
ArchLinux RISCV64★★☆☆☆★★★☆☆– package management (pacman)
– rootfs 2/4/8GB, 960MB used
– rndis / usb network
AlpineLinux RISCV64★★☆☆☆★★★☆☆– package management (apk)
– rndis / usb network
– rootfs 1GB, 150MB used
– use apk [add|update] –no-check-certificate
Ubuntu 22.04 RISCV64★☆☆☆☆★★★☆☆– package management (apt)
– rootfs 8GB, 600MB used
apt is awfully slow due 50+MB RAM requirement
Debian RISCV64★☆☆☆☆★★★☆☆package management (apt/dpkg)– no rndis (no virtual ether over usb)
– very limited amount of packages
– 4GB SD card minimum
Gentoo RISCV☆☆☆☆☆★★★☆☆– no disk image yet released
Fedora RISCV Builder☆☆☆☆☆★★★☆☆package management (rpm)– boots, but then fails with systemd (coredump)
– no login possible

Linux BuildRoot Disk Image

The BuildRoot is a minimal custom Linux release which is meant for IoT developers who know what they want and need and select the features at building time and then get a disk image which contains then a set of features and applications. It’s not very user friendly, e.g. there is no package manager which allows to add new packages afterwards once the system is running.

Download

Install

Attach a SD card to your host, find out which device it became (Linux):

% lsblk

CAUTION: make sure /dev/sdX (replace X with proper letter) is the SD card and not your other disk(s) as copying the disk image will erase and replace the content of the device you choose with the following command:

% sudo dd if=milkv-duo-v1.0.4-2023-0908.img of=/dev/sdX bs=1M; sync

then remove SD card from the host*), and insert it to the MilkV-Duo board, and power it on via USB-C.

*) in case you have the wrong /dev/sdX, or write to it if it’s not plugged in, you might struggle to write there again, simply do sudo rm /dev/sdX and then pull out and plugin the SD card again, and you should be able to write again to it.

After ~15 seconds you should be able to login with ssh root@192.168.42.1 into your MilkV-Duo with passwd: milkv or you attach a UART to USB bridge like that:

  • Pin 16/TX: RX/white UART-USB
  • Pin 17/RX: TX/green UART-USB
  • Pin 18/GND: GND/black UART-USB
  • don’t connect 5V/red UART-USB

and use tio /dev/ttyUSB0 (or another number) under Linux to connect via serial port; also useful in case bootup is stuck after doing changes, and ssh isn’t possible anymore.

Note: you won’t need to solder the male connectors, I usually just insert them loosely and the cable bending gives sufficient connectivity for a brief login to fix things and then remove UART-USB cable again.

Boot dmesg

[    0.000000] Linux version 5.10.4-tag- (ubuntu@linux) (riscv64-unknown-linux-musl-gcc (Xuantie-900 linux-5.10.4 musl gcc Toolchain V2.6.1 B-20220906) 10.2.0, GNU ld (GNU Binutils) 2.35) #1 PREEMPT Fri Sep 8 17:23:15 CST 2023
[    0.000000] earlycon: sbi0 at I/O port 0x0 (options '')
[    0.000000] printk: bootconsole [sbi0] enabled
[    0.000000] efi: UEFI not found.
[    0.000000] Ion: Ion memory setup at 0x0000000082473000 size 26 MiB
[    0.000000] OF: reserved mem: initialized node ion, compatible id ion-region
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080000000-0x0000000083f3ffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080000000-0x0000000083f3ffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080000000-0x0000000083f3ffff]
[    0.000000] On node 0 totalpages: 16192
[    0.000000]   DMA32 zone: 222 pages used for memmap
[    0.000000]   DMA32 zone: 0 pages reserved
[    0.000000]   DMA32 zone: 16192 pages, LIFO batch:3
[    0.000000] SBI specification v0.3 detected
[    0.000000] SBI implementation ID=0x1 Version=0x9
[    0.000000] SBI v0.2 TIME extension detected
[    0.000000] SBI v0.2 IPI extension detected
[    0.000000] SBI v0.2 RFENCE extension detected
[    0.000000] riscv: ISA extensions acdfimsuv
[    0.000000] riscv: ELF capabilities acdfimv
[    0.000000] pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
[    0.000000] pcpu-alloc: [0] 0 
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 15970
[    0.000000] Kernel command line: root=/dev/mmcblk0p2 rootwait rw console=ttyS0,115200 earlycon=sbi loglevel=9 riscv.fwsz=0x80000
[    0.000000] Dentry cache hash table entries: 8192 (order: 4, 65536 bytes, linear)
[    0.000000] Inode-cache hash table entries: 4096 (order: 3, 32768 bytes, linear)
[    0.000000] Sorting __ex_table...
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 29360K/64768K available (3671K kernel code, 457K rwdata, 1651K rodata, 144K init, 195K bss, 35408K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] rcu: Preemptible hierarchical RCU implementation.
[    0.000000] rcu: 	RCU event tracing is enabled.
[    0.000000] 	Trampoline variant of Tasks RCU enabled.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] riscv-intc: 64 local interrupts mapped
[    0.000000] plic: interrupt-controller@70000000: mapped 101 interrupts with 1 handlers for 2 contexts.
[    0.000000] random: get_random_bytes called from start_kernel+0x2e0/0x41c with crng_init=0
[    0.000000] riscv_timer_init_dt: Registering clocksource cpuid [0] hartid [0]
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x5c40939b5, max_idle_ns: 440795202646 ns
[    0.000009] sched_clock: 64 bits at 25MHz, resolution 40ns, wraps every 4398046511100ns
[    0.008425] Calibrating delay loop (skipped), value calculated using timer frequency.. 50.00 BogoMIPS (lpj=100000)
[    0.019128] pid_max: default: 4096 minimum: 301
[    0.024025] Mount-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.031444] Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.041119] ASID allocator initialised with 65536 entries
[    0.046849] rcu: Hierarchical SRCU implementation.
[    0.052223] EFI services will not be available.
[    0.057313] devtmpfs: initialized
[    0.066807] early_time_log: do_initcalls: 4548979us
[    0.072472] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    0.082528] futex hash table entries: 16 (order: -4, 384 bytes, linear)
[    0.089496] pinctrl core: initialized pinctrl subsystem
[    0.095414] NET: Registered protocol family 16
[    0.100480] DMA: preallocated 128 KiB GFP_KERNEL pool for atomic allocations
[    0.107813] DMA: preallocated 128 KiB GFP_KERNEL|GFP_DMA32 pool for atomic allocations
[    0.116713] thermal_sys: Registered thermal governor 'step_wise'
[    0.132538] OF: /gpio@03020000/gpio-controller@0: could not find phandle
[    0.145753] OF: /gpio@03021000/gpio-controller@1: could not find phandle
[    0.152748] OF: /gpio@03022000/gpio-controller@2: could not find phandle
[    0.159737] OF: /gpio@03023000/gpio-controller@3: could not find phandle
[    0.166722] OF: /gpio@05021000/gpio-controller@4: could not find phandle
[    0.175540] clk reset: nr_reset=64 resource_size=8
[    0.181130] get audio clk=24576000
[    0.184672] cvitek-i2s-subsys 4108000.i2s_subsys: Set clk_sdma_aud0~3 to 24576000
[    0.205405] dw_dmac 4330000.dma: CVITEK DMA Controller, 8 channels, probe done!
[    0.213805] SCSI subsystem initialized
[    0.218237] usbcore: registered new interface driver usbfs
[    0.224017] usbcore: registered new interface driver hub
[    0.229634] usbcore: registered new device driver usb
[    0.238395] Ion: ion_parse_dt_heap_common: id 0 type 2 name carveout align 1000
[    0.246344] Ion: rmem_ion_device_init: heap carveout base 0x0000000082473000 size 0x0000000001acd000 dev (____ptrval____)
[    0.257624] ion_carveout_heap_create, size=0x1acd000
[    0.262937] cvi_get_rtos_ion_size, rtos ion_size get:0x0
[    0.398272] platform carveout: [ion] add heap id 0, type 2, base 0x82473000, size 0x1acd000
[    0.407276] Advanced Linux Sound Architecture Driver Initialized.
[    0.414938] clocksource: Switched to clocksource riscv_clocksource
[    0.423536] NET: Registered protocol family 2
[    0.429180] tcp_listen_portaddr_hash hash table entries: 256 (order: 0, 4096 bytes, linear)
[    0.437895] TCP established hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.445680] TCP bind hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.452950] TCP: Hash tables configured (established 512 bind 512)
[    0.459557] UDP hash table entries: 128 (order: 0, 4096 bytes, linear)
[    0.466324] UDP-Lite hash table entries: 128 (order: 0, 4096 bytes, linear)
[    0.473794] NET: Registered protocol family 1
[    0.479012] RPC: Registered named UNIX socket transport module.
[    0.485127] RPC: Registered udp transport module.
[    0.490010] RPC: Registered tcp transport module.
[    0.494934] RPC: Registered tcp NFSv4.1 backchannel transport module.
[    0.504220] Initialise system trusted keyrings
[    0.509141] workingset: timestamp_bits=62 max_order=13 bucket_order=0
[    0.524041] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.531324] jffs2: version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
[    0.538516] Key type asymmetric registered
[    0.542733] Asymmetric key parser 'x509' registered
[    0.554087] Serial: 8250/16550 driver, 5 ports, IRQ sharing disabled
[    0.562710] printk: console [ttyS0] disabled
[    0.567239] 4140000.serial: ttyS0 at MMIO 0x4140000 (irq = 15, base_baud = 1562500) is a 16550A
[    0.576276] printk: console [ttyS0] enabled
[    0.584875] printk: bootconsole [sbi0] disabled
[    0.595241] 41c0000.serial: ttyS4 at MMIO 0x41c0000 (irq = 16, base_baud = 1562500) is a 16550A
[    0.607812] cvi-spif 10000000.cvi-spif: unrecognized JEDEC id bytes: 00 00 00 00 00 00
[    0.616044] cvi-spif 10000000.cvi-spif: device scan failed
[    0.621769] cvi-spif 10000000.cvi-spif: unable to setup flash chip
[    0.635092] libphy: Fixed MDIO Bus: probed
[    0.639938] bm-dwmac 4070000.ethernet: IRQ eth_wake_irq not found
[    0.646304] bm-dwmac 4070000.ethernet: IRQ eth_lpi not found
[    0.652286] bm-dwmac 4070000.ethernet: Hash table entries set to unexpected value 0
[    0.660361] bm-dwmac 4070000.ethernet: no reset control found
[    0.666605] bm-dwmac 4070000.ethernet: User ID: 0x10, Synopsys ID: 0x37
[    0.673525] bm-dwmac 4070000.ethernet: 	DWMAC1000
[    0.678424] bm-dwmac 4070000.ethernet: DMA HW capability register supported
[    0.685641] bm-dwmac 4070000.ethernet: RX Checksum Offload Engine supported
[    0.692858] bm-dwmac 4070000.ethernet: COE Type 2
[    0.697743] bm-dwmac 4070000.ethernet: TX Checksum insertion supported
[    0.704512] bm-dwmac 4070000.ethernet: Normal descriptors
[    0.710115] bm-dwmac 4070000.ethernet: Ring mode enabled
[    0.715630] bm-dwmac 4070000.ethernet: Enable RX Mitigation via HW Watchdog Timer
[    0.723389] bm-dwmac 4070000.ethernet: device MAC address 96:8d:89:ed:5b:ca
[    0.758200] libphy: stmmac: probed
[    0.763554] bm-dwmac 4070000.ethernet: Cannot get clk_500m_eth!
[    0.769815] bm-dwmac 4070000.ethernet: Cannot get gate_clk_axi4!
[    0.777191] dwc2 4340000.usb: axi clk installed
[    0.781982] dwc2 4340000.usb: apb clk installed
[    0.786726] dwc2 4340000.usb: 125m clk installed
[    0.791530] dwc2 4340000.usb: 33k clk installed
[    0.796239] dwc2 4340000.usb: 12m clk installed
[    0.801042] dwc2 4340000.usb: EPs: 8, dedicated fifos, 3072 entries in SPRAM
[    0.808950] dwc2 4340000.usb: DWC OTG Controller
[    0.813824] dwc2 4340000.usb: new USB bus registered, assigned bus number 1
[    0.821096] dwc2 4340000.usb: irq 36, io mem 0x04340000
[    0.827545] hub 1-0:1.0: USB hub found
[    0.831570] hub 1-0:1.0: 1 port detected
[    0.837416] usbcore: registered new interface driver usb-storage
[    0.844106] i2c /dev entries driver
[    0.849865] sdhci: Secure Digital Host Controller Interface driver
[    0.856316] sdhci: Copyright(c) Pierre Ossman
[    0.860841] sdhci-pltfm: SDHCI platform and OF driver helper
[    0.867007] cvi:sdhci_cvi_probe
[    0.914963] mmc0: SDHCI controller on 4310000.cv-sd [4310000.cv-sd] using ADMA 64-bit
[    0.923131] cvi_proc_init cvi_host 0x(____ptrval____)
[    0.929146] usbcore: registered new interface driver usbhid
[    0.938965] usbhid: USB HID core driver
[    0.944921] cvitek-i2s 4100000.i2s: cvi_i2s_probe
[    0.955198] cvitek-i2s 4130000.i2s: cvi_i2s_probe
[    0.960922] cviteka-adc sound_adc: cviteka_adc_probe, dev name=sound_adc
[    0.968044] cviteka-adc sound_adc: cviteka_adc_probe start devm_snd_soc_register_card
[    0.976650] cvitekaadc 300a100.adc: cvitekaadc_probe
[    0.987619] cviteka-dac sound_dac: cviteka_dac_probe, dev name=sound_dac
[    0.995091] cvitekadac 300a000.dac: cvitekadac_probe
[    1.000685] cvitekadac_probe gpio_is_valid mute_pin_l
[    1.006585] NET: Registered protocol family 17
[    1.011477] Loading compiled-in X.509 certificates
[    1.039015] mmc0: new SDHC card at address 0001
[    1.051483] mmcblk0: mmc0:0001 MSD20 14.6 GiB 
[    1.061126] cviteka-adc sound_adc: cviteka_adc_probe, dev name=sound_adc
[    1.068302] cviteka-adc sound_adc: cviteka_adc_probe start devm_snd_soc_register_card
[    1.080491]  mmcblk0: p1 p2 p3
[    1.088431] cviteka-dac sound_dac: cviteka_dac_probe, dev name=sound_dac
[    1.101557] cfg80211: Loading compiled-in X.509 certificates for regulatory database
[    1.112515] cfg80211: Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7'
[    1.119630] cfg80211: failed to load regulatory.db
[    1.124824] ALSA device list:
[    1.128383] dw-apb-uart 4140000.serial: forbid DMA for kernel console
[    1.148546] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null)
[    1.157087] VFS: Mounted root (ext4 filesystem) on device 179:2.
[    1.166212] devtmpfs: mounted
[    1.169547] Freeing unused kernel memory: 144K
[    1.174226] Kernel memory protection not selected by kernel config.
[    1.180757] Run /sbin/init as init process
[    1.185024]   with arguments:
[    1.188116]     /sbin/init
[    1.190917]   with environment:
[    1.194189]     HOME=/
[    1.196652]     TERM=linux
[    1.199477] early_time_log: run_init_process: 5681657us
[    1.307708] EXT4-fs (mmcblk0p2): re-mounted. Opts: errors=remount-ro
[    1.395862] random: fast init done
[    1.511578] random: dd: uninitialized urandom read (512 bytes read)
[    1.725888] random: dhcpcd: uninitialized urandom read (112 bytes read)
[    1.817530] bm-dwmac 4070000.ethernet eth0: PHY [stmmac-0:00] driver [Generic PHY] (irq=POLL)
[    1.839025] dwmac1000: Master AXI performs any burst length
[    1.845702] bm-dwmac 4070000.ethernet eth0: No Safety Features support found
[    1.853923] bm-dwmac 4070000.ethernet eth0: IEEE 1588-2002 Timestamp supported
[    1.862474] bm-dwmac 4070000.ethernet eth0: configuring for phy/rmii link mode
[    7.195361] random: dnsmasq: uninitialized urandom read (128 bytes read)
[    7.202528] random: dnsmasq: uninitialized urandom read (48 bytes read)
[    7.250085] cv180x_sys: bad vermagic: kernel tainted.
[    7.255425] Disabling lock debugging due to kernel taint
[    7.261336] cv180x_sys: loading out-of-tree module taints kernel.
[    7.294527] res-reg: start: 0xa0c8000, end: 0xa0c801f, virt-addr(ffffffd0040c9000).
[    7.303316] CVITEK CHIP ID = 22
[    7.318433] cvi_rtos_cmdqu_probe start ---
[    7.322806] name=1900000.rtos_cmdqu
[    7.327194] res-reg: start: 0x1900000, end: 0x1900fff, virt-addr(ffffffd004228000).
[    7.335254] cvi_rtos_cmdqu_probe DONE
[    7.339691] [cvi_spinlock_init] success
[    7.377509] cif a0c2000.cif: cam0 clk installed
[    7.382321] cif a0c2000.cif: cam1 clk installed
[    7.387481] cif a0c2000.cif: vip_sys_2 clk installed
[    7.392945] cif a0c2000.cif: clk_mipimpll clk installed (____ptrval____)
[    7.400203] cif a0c2000.cif: clk_disppll clk installed (____ptrval____)
[    7.407370] cif a0c2000.cif: clk_fpll clk installed (____ptrval____)
[    7.414298] cif a0c2000.cif: (0) res-reg: start: 0xa0c2000, end: 0xa0c3fff.
[    7.421797] cif a0c2000.cif:  virt-addr((____ptrval____))
[    7.427690] cif a0c2000.cif: (1) res-reg: start: 0xa0d0000, end: 0xa0d0fff.
[    7.435183] cif a0c2000.cif:  virt-addr((____ptrval____))
[    7.441074] cif a0c2000.cif: (2) res-reg: start: 0xa0c4000, end: 0xa0c5fff.
[    7.448559] cif a0c2000.cif:  virt-addr((____ptrval____))
[    7.454448] cif a0c2000.cif: (3) res-reg: start: 0x3001c30, end: 0x3001c5f.
[    7.461933] cif a0c2000.cif:  virt-addr((____ptrval____))
[    7.467824] cif a0c2000.cif: no pad_ctrl for cif
[    7.472974] cif a0c2000.cif: request irq-26 as cif-irq0
[    7.478759] cif a0c2000.cif: request irq-27 as cif-irq1
[    7.484514] cif a0c2000.cif: rst_pin = 424, pol = 1
[    7.499342] snsr_i2c snsr_i2c: i2c:-------hook 0
[    7.504362] snsr_i2c snsr_i2c: i2c:-------hook 1
[    7.509771] snsr_i2c snsr_i2c: i2c:-------hook 2
[    7.515073] snsr_i2c snsr_i2c: i2c:-------hook 3
[    7.519993] snsr_i2c snsr_i2c: i2c:-------hook 4
[    7.596322] vi_core_probe:203(): res-reg: start: 0xa000000, end: 0xa07ffff, virt-addr(ffffffd004400000).
[    7.606219] vi_core_probe:216(): irq(28) for isp get from platform driver.
[    7.614260] vi_tuning_buf_setup:253(): tuning fe_addr[0]=0x8183f490, be_addr[0]=0x81837290, post_addr[0]=0x81820000
[    7.625482] vi_tuning_buf_setup:253(): tuning fe_addr[1]=0x8193f490, be_addr[1]=0x81937290, post_addr[1]=0x81920000
[    7.636654] vi_tuning_buf_setup:253(): tuning fe_addr[2]=0x8185f490, be_addr[2]=0x81857290, post_addr[2]=0x81840000
[    7.647773] sync_task_init:177(): sync_task_init vi_pipe 0
[    7.653739] sync_task_init:177(): sync_task_init vi_pipe 1
[    7.659725] sync_task_init:177(): sync_task_init vi_pipe 2
[    7.666173] vi_core_probe:252(): isp registered as cvi-vi
[    7.750302] cvi_dwa_probe:487(): done with rc(0).
[    7.791388] cv180x-cooling cv180x_cooling: elems of dev-freqs=6
[    7.797661] cv180x-cooling cv180x_cooling: dev_freqs[0]: 850000000 500000000
[    7.805487] cv180x-cooling cv180x_cooling: dev_freqs[1]: 425000000 375000000
[    7.813089] cv180x-cooling cv180x_cooling: dev_freqs[2]: 425000000 300000000
[    7.820782] cv180x-cooling cv180x_cooling: Cooling device registered: cv180x_cooling
[    7.866285] jpu ctrl reg pa = 0xb030000, va = (____ptrval____), size = 256
[    7.873985] end jpu_init result = 0x0
[    8.065890] cvi_vc_drv_init result = 0x0
[    8.080795] sh (172): drop_caches: 3
[    8.261909] using random self ethernet address
[    8.261928] using random host ethernet address
[    8.309722] usb0: HOST MAC 6e:54:6b:61:a9:f5
[    8.312146] usb0: MAC ce:b2:db:da:82:7b
[    8.312220] dwc2 4340000.usb: bound driver configfs-gadget
[    8.346187] dwc2 4340000.usb: new device is high-speed
[    8.391171] dwc2 4340000.usb: new device is high-speed
[    8.682141] dwc2 4340000.usb: new device is high-speed
[    8.750359] dwc2 4340000.usb: new address 14
[   57.067015] random: crng init done

Note: usb0: HOST MAC and MAC are set randomly and change after each (re)boot.

/proc/cpuinfo

[root@milkv-duo]~# more /proc/cpuinfo 
processor       : 0
hart            : 0
isa             : rv64imafdvcsu
mmu             : sv39

df

[root@milkv-duo]~# df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/root               763327    157774    562136  22% /
devtmpfs                 14680         0     14680   0% /dev
tmpfs                    14752         0     14752   0% /dev/shm
tmpfs                    14752        52     14700   0% /tmp
tmpfs                    14752        28     14724   0% /run

[root@milkv-duo]~# df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root               745.4M    154.1M    549.0M  22% /
devtmpfs                 14.3M         0     14.3M   0% /dev
tmpfs                    14.4M         0     14.4M   0% /dev/shm
tmpfs                    14.4M     52.0K     14.4M   0% /tmp
tmpfs                    14.4M     28.0K     14.4M   0% /run

ps

PID   USER     COMMAND
    1 root     init
    2 root     [kthreadd]
    3 root     [rcu_gp]
    4 root     [rcu_par_gp]
    5 root     [kworker/0:0-eve]
    7 root     [kworker/u2:0-ev]
    8 root     [mm_percpu_wq]
    9 root     [ksoftirqd/0]
   10 root     [rcu_preempt]
   11 root     [kdevtmpfs]
   12 root     [rcu_tasks_kthre]
   13 root     [oom_reaper]
   14 root     [writeback]
   15 root     [kcompactd0]
   24 root     [kblockd]
   25 root     [watchdogd]
   27 root     [kworker/0:1H-mm]
   28 root     [rpciod]
   29 root     [kworker/u3:0]
   30 root     [xprtiod]
   31 root     [cfg80211]
   32 root     [kswapd0]
   33 root     [nfsiod]
   34 root     [spi0]
   35 root     [spi1]
   36 root     [stmmac_wq]
   37 root     [kworker/u2:1]
   70 root     [irq/45-cviusb-o]
   71 root     [irq/46-cd-gpio-]
   72 root     [sdhci]
   73 root     [irq/23-mmc0]
   75 root     [ion_system_heap]
   76 root     [mmc_complete]
   81 root     [jbd2/mmcblk0p2-]
   82 root     [ext4-rsv-conver]
   97 root     /sbin/syslogd -n
  101 root     /sbin/klogd -n
  131 dhcpcd   dhcpcd: [master] [ip4]
  132 root     dhcpcd: [privileged actioneer]
  133 dhcpcd   dhcpcd: [network proxy]
  134 dhcpcd   dhcpcd: [control proxy]
  147 root     [kworker/0:3-eve]
  156 root     /usr/sbin/ntpd -g -p /var/run/ntpd.pid
  165 root     /usr/sbin/dropbear -R
  170 nobody   /usr/sbin/dnsmasq
  180 root     [cvitask_isp_pre]
  181 root     [cvitask_isp_bla]
  182 root     [cvitask_isp_err]
  184 root     [cvitask_vpss_0]
  185 root     [cvitask_vpss_1]
  187 root     [gdc_work]
  192 root     [cvitask_tpu_wor]
  198 root     {S99user} /bin/sh /etc/init.d/S99user start
  199 root     [kworker/0:2H]
  211 root     -sh
 1297 root     /usr/sbin/dropbear -R
 1302 root     -sh
 1331 root     sleep 0.5
 1332 root     ps

/bin

[root@milkv-duo]~# ls /bin
arch           dmesg          linux64        nuke           sleep
ash            dnsdomainname  ln             pidof          stty
base32         dumpkmap       login          ping           su
base64         echo           ls             pipe_progress  sync
busybox        egrep          lsattr         printenv       tar
cat            false          mk_cmds        ps             touch
chattr         fdflush        mkdir          pwd            true
chgrp          fgrep          mknod          resume         umount
chmod          getopt         mktemp         rm             uname
chown          grep           more           rmdir          usleep
compile_et     gunzip         mount          run-parts      vi
cp             gzip           mountpoint     sed            watch
cpio           hostname       mt             setarch        zcat
date           kill           mv             setpriv
dd             link           netstat        setserial
df             linux32        nice           sh

/usr/bin

[root@milkv-duo]~# ls /usr/bin/
[                  fold               od                 tee
[[                 free               openvt             telnet
ar                 fuser              passwd             test
ascii              gcore              paste              tftp
awk                gdb                patch              time
basename           gdb-add-index      pip                top
bc                 head               pip3               tr
bunzip2            hexdump            pip3.9             traceroute
bzcat              hexedit            printf             truncate
chrt               hostid             pyserial-miniterm  ts
chvt               htop               pyserial-ports     tty
cksum              id                 python             uniq
clear              install            python3            unix2dos
cmp                ipcrm              python3.9          unlink
crc32              ipcs               readlink           unlzma
crontab            killall            realpath           unlzop
cut                last               renice             unxz
cvi_pinmux         less               reset              unzip
dbclient           logger             resize             uptime
dc                 logname            scp                uudecode
deallocvt          lsof               seq                uuencode
diff               lspci              setfattr           vlock
dirname            lsscsi             setkeycodes        w
dos2unix           lsusb              setsid             wc
dropbearconvert    lzcat              sha1sum            wget
dropbearkey        lzma               sha256sum          which
du                 lzopcat            sha3sum            who
easy_install       md5sum             sha512sum          whoami
easy_install-3.9   mesg               shred              xargs
eject              microcom           smtpd.py.9         xmlcatalog
env                mkfifo             sort               xmllint
event_rpcgen.py    mkpasswd           ssh                xmlwf
evtest             nl                 strace             xsltproc
expr               nohup              strace-log-merge   xxd
factor             nproc              strings            xz
fallocate          nslookup           svc                xzcat
find               ntpdate            svok               yes
flock              ntptime            tail

[root@milkv-duo]~# ls -1 /usr/bin/ | wc -l
151

Multiple Milk-V Duos / Alternative IPs

In order to support multiple Milk-V Duos on the same host via USB-C, you assign for each board its own network:

  • board 1: 192.168.51.0
  • board 2: 192.168.52.0
  • board 3: 192.168.53.0

Edit on each board two files:

/mnt/system/usb-rndis.sh (buildroot-based) or /etc/usb-rndis.sh (other systems):

ifconfig usb0 192.168.51.1

/etc/dnsmasq.conf:

dhcp-range=192.168.51.2,192.168.51.242,1h

In order to add a new board, you login into 192.168.42.1 as usual, and change it to the 192.168.52.1 and so on.

Resizing Disk

By default the entire available space of the SD card is only 1GB (or 2GB in case you use another distro), but you can make the rest of the SD card available to /data for example – part of the guide was taken from a post in the forum but updated it:

% mkdir /data
% fdisk /dev/mmcblk0
n (new partition)
p (primary partition)
4
<RETURN> (confirm start selection)
<RETURN> (confirm end selection)
w
q

% reboot

and login again, continue with:

% mkfs.ext4 /dev/mmcblk0p4
% echo "/dev/mmcblk0p4 /data ext4 defaults 0 0" >> /etc/fstab
% reboot

once you login again, you see the new available space:

% df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root               745.4M    154.1M    549.0M  22% /
devtmpfs                 14.3M         0     14.3M   0% /dev
tmpfs                    14.4M         0     14.4M   0% /dev/shm
tmpfs                    14.4M     52.0K     14.4M   0% /tmp
tmpfs                    14.4M     28.0K     14.4M   0% /run
/dev/mmcblk0p4           13.4G     24.0K     12.7G   0% /data

Making Swap Space

pip won’t work by default, as there is too little memory to work – so you can make swap space in two ways:

mmcblk0p3: unused 256M partition

As of system image v1.0.4 there is an unused partition you can activate:

% mkswap /dev/mmcblk0p3
% swapon /dev/mmcblk0p3
% echo "/dev/mmcblk0p3 swap swap defaults 0 0" >> /etc/fstab

Swapfile

Or you can create a 256M swapfile to increase available memory, given we claimed the rest of the SD card as /data as previously shown:

% cd /data
% fallocate -l 256M swapfile
% chmod 600 swapfile
% mkswap swapfile
% swapon swapfile

and to make it permanent:

% echo "/data/swapfile swap swap defaults 0 0" >> /etc/fstab

ArchLinux Disk Image

I followed this guide to get ArchLinux working – thanks to Judehahh doing the main work – and added RNDIS support (Virtual Ethernet over USB) and made a disk image to use, the date e.g. “2023-10-09” references the riscv64 rootfs date which was unpacked as a base, the “x.xgb” describes the size of disk or rootfs and “vX.X.X” the actual release version. Unzip downloaded image first before writing on the SD card.

ssh root@192.168.42.1
passwd: milkv

Note: these are very experimental disk images.

Milk-V Duo 256M

Disk ImageFeaturesIncluded
milkv-duo-256m-archlinux-riscv64-2023-10-09-7.0gb-v0.0.3-spiritdude.img
md5:218c1fb27addb4d2a7d455a5c9b8ea7a
BuildRoot v1.0.4
2024/02/15
– 240MB RAM (no camera support)
– 250MB swap enabled
– RNDIS (connect via usb virtual ether)
– minimum 8GB SD card
5.7GB free in rootfs
– persistent MAC addresses for RNDIS (internet routing ready)
– pacman (pkg mgr)
– ssh (dropbear v2022.83)
– python 3.11
– make
– tinycc/tcc
– lua/luac
– lighttpd

For the 256m-version disk image I downloaded the kernel from this thread and just copied boot.sd and fip.bin into the first partition, otherwise no changes toward the milkv-duo disk-image (below) was made

Milk-V Duo (64MB)

Disk ImageFeaturesIncluded
milkv-duo-archlinux-riscv64-2023-10-09-7.0gb-v0.0.3-spiritdude.img
md5:8c217f01dac7689f1f61ad9e1674913d
BuildRoot v1.0.4
2023/11/02
– 55MB RAM (no camera support)
– 250MB swap enabled
– RNDIS (connect via usb virtual ether)
– minimum 8GB SD card
5.7GB free in rootfs
– persistent MAC addresses for RNDIS (internet routing ready)
– pacman (pkg mgr)
– ssh (dropbear v2022.83)
– python 3.11
– make
– tinycc/tcc
– lua/luac
– lighttpd
milkv-duo-archlinux-riscv64-2023-10-09-7.0gb-v0.0.2-spiritdude.img
md5:b9ed3f8fb036f74733e2806dff9582bb
BuildRoot v1.0.4
2023/10/25
– 55MB RAM (no camera support)
– 250MB swap enabled
– RNDIS (connect via usb virtual ether)
– minimum 8GB SD card
5.7GB free in rootfs
– pacman (pkg mgr)
– ssh (dropbear v2022.83)
– python 3.11
– make
– tinycc/tcc
– lua/luac
– lighttpd
milkv-duo-archlinux-riscv64-2023-10-09-7.0gb-v0.0.1-spiritdude.img
md5:a137a86a0dad572014afe2f2eda78a2c
BuildRoot v1.0.4
2023/10/24
– 55MB RAM (no camera support)
– 250MB swap enabled
– RNDIS (connect via usb virtual ether)
– minimum 8GB SD card
5.7GB free in rootfs
– pacman (pkg mgr)
– ssh (dropbear v2022.83)
– python 3.11
– make
– tinycc/tcc
– lua/luac
– lighttpd
milkv-duo-archlinux-riscv64-2023-10-09-2.5gb-v0.0.1-spiritdude.img
md5:2ff352bcb7a9d855dfe50b4d3176f69c
BuildRoot v1.0.4
2023/10/21
– 55MB RAM (no camera support)
– 250MB swap enabled
– RNDIS (connect via usb virtual ether)
– minimum 4GB SD card
– 960 apps in /usr/bin/
1GB free in rootfs
– pacman (pkg mgr)
– ssh (dropbear v2022.83)
– python 3.11
– make
– tinycc/tcc
– lua/luac
– lighttpd
milkv-duo-archlinux-riscv64-2023-10-09-1.5gb-v0.0.1-spiritdude.img
md5:ecc2ab2076ad9fbec795c5dba73a291e
BuildRoot v1.0.4
2023/10/21
– 55MB RAM (no camera support)
– 250MB swap enabled
– RNDIS (connect via usb virtual ether)
– minimum 2GB SD card
– 960 apps in /usr/bin/
only 40MB free in rootfs (!!)
– pacman (pkg mgr)
– ssh (dropbear v2022.83)
– python 3.11
– make
– tinycc/tcc
– lua/luac
– lighttpd

You have a full Linux system with 55MB RAM available . . .

Notes:

  • pacman -Fy 'term' fails for me (too much memory needed), instead run gzip -d -c /var/lib/pacman/sync/*.files | grep -ai 'term'

AlpineLinux Disk Image

As this discussion thread, Chaiwat Suttipongsakul did the disk image hosted on github, I made a mirror and added date of his ‘cwt’ handle to distinct it from future releases:

Milk-V Duo 256M

DISK IMAGEFeaturesIncluded
milkv-duo-256m-alpinelinux-cwt-2023-10-28.img
md5: 057d0304d958de7b7323d6d963d801e2
2024-02-18
– package management (apk)
– rndis / usb network (but random MAC addresses)
– rootfs 1GB, 150MB used
– dropbear v2022.83

– use apk [add|update] –no-check-certificate

For the 256m-version disk image I downloaded the kernel from this thread and just copied boot.sd and fip.bin into the first partition, otherwise no changes toward the milkv-duo disk-image (below) was made.

Milk-V Duo (64MB)

Disk ImageFeaturesincluded
milkv-duo-alpinelinux-cwt-2023-10-28.img
md5: dbc23ca7b372ce7718924067ac846693
2023-10-28
– package management (apk)
– rndis / usb network (but random MAC addresses)
– rootfs 1GB, 150MB used
– dropbear v2022.83

– use apk [add|update] –no-check-certificate

Notes:

  • use apk [add|update] --no-check-certificate to install new packages, otherwise installs or update fail

Ubuntu Disk Image

I followed this guide (see discussion thread as well) – thanks to Bassusteur – and added RNDIS related services so you can login with ssh root@192.168.42.1 (passwd milkv) via virtual Ethernet over USB; unzip disk image before you write on the SD card.

Notes:

  • apt / apt-get are awfully slow on Milk-V Duo at step “Building dependency tree...“, takes 4+mins for each apt install call as apt requires 50+MB RAM to build that dependency tree, which goes hard on all available RAM + swap
  • these are very experimental disk images

Note: milkv-duo (64MB RAM) vs milkv-duo-256m (256MB RAM)

Milk-V Duo 256M

Disk ImageFeaturesIncluded
milkv-duo-256m-ubuntu-22.04-riscv64-v0.0.4-spiritdude.img
md5: 0a80c70d9a6b2e763f49b7dca7aba59f
BuildRoot v1.0.5
2024/02/15
– 240MB RAM (no camera support)
– RNDIS (connect via usb virtual ether)
– persistent MAC addresses for RNDIS (internet routing ready)
– apt works now smoothly
– minimum 8GB SD card
6.0GB free in rootfs
– dropbear (v2020.81)
– python 3.10.12

For the 256m-version disk image I downloaded the kernel from this thread and just copied boot.sd and fip.bin into the first partition, otherwise no changes toward the milkv-duo disk-image (below) was made.

Milk-V Duo (64MB)

Disk ImageFeaturesIncluded
milkv-duo-ubuntu-22.04-riscv64-v0.0.4-spiritdude.img
md5: 21833b5041e4f6a1706e910c491ba2d3
BuildRoot v1.0.5
2023/11/22
– 55MB RAM (no camera support)
– 250MB swap enabled
– RNDIS (connect via usb virtual ether)
– persistent MAC addresses for RNDIS (internet routing ready)
– minimum 8GB SD card
6.0GB free in rootfs
– dropbear (v2020.81)
– python 3.10.12
– zram enabled in kernel, and works now, but apt still very slow
milkv-duo-ubuntu-22.04-riscv64-v0.0.3-spiritdude.img
md5: 9f8d3ab61a7ea328b8da5217f020c2b2
BuildRoot v1.0.5
2023/11/21
– 55MB RAM (no camera support)
– 250MB swap enabled
– RNDIS (connect via usb virtual ether)
– persistent MAC addresses for RNDIS (internet routing ready)
– minimum 8GB SD card
6.0GB free in rootfs
– dropbear (v2020.81)
– python 3.10.12
– zram not enabled in kernel yet
milkv-duo-ubuntu-22.04-riscv64-v0.0.2-spiritdude.img
md5: bb05d70a0e81169ef2af05b68ef82ced
BuildRoot v1.0.4
2023/11/20
– 55MB RAM (no camera support)
– 250MB swap enabled
– RNDIS (connect via usb virtual ether)
– persistent MAC addresses for RNDIS (internet routing ready)
– minimum 8GB SD card
6.0GB free in rootfs
– dropbear (v2020.81)
– python 3.10.12

Changes done compared to this guide:

  • removed dhcpcd as is clashes with dnsmasq
  • added RNDIS related scripts, incl. dnsmasq and rndis.service
  • disabled systemd-resolved service due clash with dnsmasq
  • added /etc/resolv.conf.tail with default DNS servers
  • installed dropbear (lightweight sshd), removed the auto generated keys, added in /etc/default/dropbear the -R switch so new keys are generated at first boot

Tips & Examples

Most of the examples relate to the Duo BuildRoot SDK setup, but should be easily adaptable to ArchLinux or other distros.

blink.py with sysfs GPIO

There is a way to control GPIO via sysfs with Python:

Note: you require more memory to run pip, use guide Make Swap Space (previous section) then proceed:

% cd gpio-1.0.0
% pip install .
% chmod +r /sys/class/gpio/export

then use this script blink.py:

import time

import gpio as GPIO

pin = 440
GPIO.setup(pin, GPIO.OUT)

while True:
    GPIO.output(pin, GPIO.HIGH)
    time.sleep(1.0)
    GPIO.output(pin, GPIO.LOW)
    time.sleep(1.0)

and run it:

% python blink.py

See this table for GPIO names, pins and numbers, a copy (2023/10/10):

GPIO NAMEGPIO PINGPIO NumberNotes
GPIOA1419494
GPIOA1520495
GPIOA1616496
GPIOA1717497
GPIOA2224502
GPIOA2321503
GPIOA2422504
GPIOA2525505
GPIOA2627506
GPIOA2726507
GPIOA281508
GPIOA292509
GPIOC9144251.8V
GPIOC10154261.8V
PWR_GPIO4293561.8V
PWR_GPIO1812370
PWR_GPIO196371
PWR_GPIO207372
PWR_GPIO2111373
PWR_GPIO2210374
PWR_GPIO239375
PWR_GPIO255377
PWR_GPIO264378

Pinpong

Follow the guide to install pinpong.zip (my mirror), only for V1.0.4 system image, and then:

blink2.py

import time
from pinpong.board import Board,Pin

Board("MILKV-DUO").begin()

led = Pin(Pin.D0, Pin.OUT)

while True:
  led.value(1)
  time.sleep(1)
  led.value(0)
  time.sleep(1)

The pinpong library covers quite a lot of functionality, and useful examples:

root@milkv-duo2]~# ls /usr/lib/python3.9/site-packages/pinpong/examples/milkv-Duo/
__init__.py               gravityPM2.5.py           oled2864.pyc
__init__.pyc              i2c.py                    ozone.py
adc.py                    i2c.pyc                   ozone.pyc
adc.pyc                   i2c_scan.py               paj7620.py
as7341.py                 i2c_scan.pyc              paj7620.pyc
as7341.pyc                iic_to_serial.py          ph1.py
blink.py                  iic_to_serial.pyc         ph1.pyc
blink.pyc                 ir_recv.py                ph2.py
bme280.py                 ir_recv.pyc               ph2.pyc
bme280.pyc                ir_send.py                pwm.py
bme680.py                 ir_send.pyc               pwm.pyc
bme680.pyc                irq.py                    rgb_panel.py
bmi160_acc.py             irq.pyc                   rgb_panel.pyc
bmi160_acc.pyc            lcd1602.py                sen0483.py
bmi160_step.py            lcd1602.pyc               sen0483.pyc
bmi160_step.pyc           lis2dh.py                 servo.py
bmp280.py                 lis2dh.pyc                servo.pyc
bmp280.pyc                max30103.py               sht31.py
bmp388.py                 max30103.pyc              sht31.pyc
bmp388.pyc                mics_enable_power.py      speech_synthesis.py
button.py                 mics_enable_power.pyc     speech_synthesis.pyc
button.pyc                mics_get_adc_data.py      spi.py
buzzer.py                 mics_get_adc_data.pyc     spi.pyc
buzzer.pyc                mics_get_gas_exist.py     sr04_urm10.py
ccs811_read_baseline.py   mics_get_gas_exist.pyc    sr04_urm10.pyc
ccs811_read_baseline.pyc  mics_get_gas_ppm.py       st7789-as7341.py
ccs811_read_data.py       mics_get_gas_ppm.pyc      st7789.py
ccs811_read_data.pyc      mlx90614.py               st7789.pyc
dht.py                    mlx90614.pyc              tcs34725.py
dht.pyc                   mp3.py                    tcs34725.pyc
dht20.py                  mp3.pyc                   tds.py
dht20.pyc                 neopixel.py               tds.pyc
ds0469.py                 neopixel.pyc              tone.py
ds0469.pyc                nfc.py                    tone.pyc
ds1307.py                 nfc.pyc                   uart.py
ds1307.pyc                nfc_card_info.py          uart.pyc
ds18b20.py                nfc_card_info.pyc         urm09.py
ds18b20.pyc               nfc_uart.py               urm09.pyc
ens160.py                 nfc_uart.pyc              vl53l0.py
ens160.pyc                nfc_uart_card.py          vl53l0.pyc
gp2y1010au0f.py           nfc_uart_card.pyc
gp2y1010au0f.pyc          oled2864.py

I will explore those examples.

Start Script at Boot

As of V1.0.4 system image, the /etc/init.d/S99user executes /mnt/data/auto.sh if it exists:

% mkdir /mnt/data
% vi /mnt/data/auto.sh

and list each of the processes:

/path/to/my/script.sh &
python /path/to/another/script.py &

TinyCC

According this post, tinycc has been ported as well – C compiler and C interpreter in one – download the .zip (my mirror) and run its install.sh, and then fix missing executable bit:

% chmod +x /usr/local/bin/tcc

and start playing with test.c:

#!/usr/local/bin/tcc -run

#include <stdio.h>

int main(int argc, char **argv) {
    printf("%s---\n", "hello milk-v!");
    return 0;
}
% tcc -o test test.c
% ./test
hello milk-v!---
% chmod +x test.c
% ./test.c
hello milk-v!---

Thanks to Yang who ported and provided the download.

I2C: SSD1306 OLED

I followed the example, and compiled the sources on the board itself with tcc:

tcc -r ssd1306.c -I .
tcc -r linux_i2c.c -I .
tcc -o ssd1306 main.c linux_i2c.o ssd1306.o -I .
./ssd1306 -I 128x64
./ssd1306 -c
./ssd1306 -m "Hello world!\nMilk-V Duo"
OLED SSD1306 connected to Milk-V Duo via I2C, controlled via RISC-V binary ssd1306 from ssd1306_linux

Software State

On-BoardBuildRoot v1.0.4-2023-0908BUILDROOT V1.0.4-2023-1017-spiritdudeArchLinux 2023-10-09 v0.0.1 spiritdude
available memory (RAM)48MB55MB55MB
ssh serverok (dropbear v2020.81)ok (dropbear v2020.81)ok (dropbear v2022.83)
ssh clientok (dropbear v2020.81)ok (dropbear v2020.81)ok (dropbear v2022.83)
pythonok (python 3.9.5)ok (python 3.9.5)ok (python 3.11)
py web-server (with socket)okokok
py gpio (with pinpong or gpio)okok
py spi (with built-in spidev)available, not yet testedavailable, not yet tested
py i2c (with pinpong)available, not yet tested with pinpong
ok with .c (see above section)
available, not yet tested with pinpong
ok with .c (see above section)
py pwm (with pinpong)not found, /sys/class/pwm/* is emptynot found, /sys/class/pwm/* is empty
pipfailed (hangs), enable swap space to use itbarely works, enable swap spaceworks (swap space enabled)
cc/gcc/clangnot foundnot includednot included
tinycc/tccok, see this post how to installok, see this post how to installok
rsyncnot foundincludedincluded
wgetok, but no https (only http, ftp)ok, but no https (only http, ftp)not included, installable
lua/luaclua (5.4.6): segmentation fault, luac (5.4.6): seems to work (both compiled with tinycc) lua & luac (5.3.6) workslua & luac (5.4.6) works
extrasquickjs/qjs, micropython, nano, screen, git, make (no gcc/cc, use tinycc), thttpd, nginx, lighttpd, php-cgi, file, which, sudopacman (package mgr), lighttpd, file, which, sudo, make

Internet Access for Milk-V Duo

RNDIS (Virtual Ethernet over USB)

The host has to run Ethernet over USB and also operate as transparent router and let the connected board(s) reach the internet, see this guide (use google translate to english), here the brief description:

On The Host

The outgoing_if is the outgoing interface, either eth0 or wpl0s0 or something, check with ifconfig of the proper name, and then as root perform:

% sysctl net.ipv4.ip_forward=1
% iptables -P FORWARD ACCEPT
% iptables -t nat -A POSTROUTING -o outgoing_if -j MASQUERADE

Also, find out which IP your host got (ip_of_host) from the connected board, e.g. 192.168.42.120, also check with ifconfig.

On The Board

% ip r add default via ip_of_host
% echo "nameserver 8.8.8.8" >> /etc/resolv.conf

Static IP for Host with RNDIS

What may look simple actually isn’t that easy as RNDIS itself is the culprit:

  • one can limit the IP range in dnsmasq.conf to from/to be the same IP, but
  • as RNDIS assigns random MAC addresses to the RNDIS host (the board) and the RNDIS client (the host the board is connected to) treat it as new device at every boot – if you force it to have the same IP again, the host will not get a new IP via DHCP as it has the IP remembered for another MAC address . . .
  • it’s a mess

So, a working solution is:

/etc/rndis-macs.sh which generates two random MAC addresses but keeps them persistent then:

#!/bin/bash

RNDIS_USB="/tmp/usb/usb_gadget/cvitek/functions/rndis.usb0"
MAC_FILE="/etc/rndis-macs.conf"

generate_random_mac() {
    printf "02:%02x:%02x:%02x:%02x:%02x\n" $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256))
}

# check if the MAC address file exists and has exactly two lines
if [[ -f "$MAC_FILE" ]] && [[ $(wc -l < "$MAC_FILE") -eq 2 ]]; then
    # read the two MAC addresses from the file
    IFS=$'\n' read -d '' -r -a macs < "$MAC_FILE"
    dev="${macs[0]}"
    host="${macs[1]}"
    echo "using existing MAC addresses:"
else
    # generate two new MAC addresses and store them in the file
    echo "generating new MAC addresses:"
    dev=$(generate_random_mac)
    host=$(generate_random_mac)
    echo "$dev" > "$MAC_FILE"
    echo "$host" >> "$MAC_FILE"
fi
echo "dev_addr: $dev"
echo "host_addr: $host"
echo "$dev" > "$RNDIS_USB"/dev_addr
echo "$host" > "$RNDIS_USB"/host_addr

/etc/usb-rndis.sh:

/etc/uhubon.sh device >> /tmp/rndis.log 2>&1
/etc/run_usb.sh probe rndis >> /tmp/rndis.log 2>&1
/etc/rndis-macs.sh >> /tmp/rndis.log 2>&1
/etc/run_usb.sh start rndis >> /tmp/rndis.log 2>&1
sleep 0.5
ip link set dev usb0 up
ip a add 192.168.42.1/24 dev usb0
ip r add default via 192.168.42.2
sleep 0.5
systemctl start dnsmasq

/etc/dnsmasq.conf

interface=usb0
dhcp-range=192.168.42.2,192.168.42.2,1h
dhcp-option=3
dhcp-option=6

Note: if you had 192.168.42.2 once assigned to the host, choose another “static” IP in your range, as your host has remembered the IP to a particular MAC address and won’t accept it again, e.g. 192.168.42.200 or so.

As of milkv-duo-archlinux-*-v0.0.3-spiritdude.img the above solution is included, see list of ArchLinux disk images.

Custom BuildRoot Milk-V Duo Disk Image

Here my brief guide – see also this guide (use google translate) – how to customize packages included in the base distribution of the image for the SD card:

% cd duo-buildroot-sdk/buildroot-2021.05
% make menuconfig

then go into the “Target packages”, and then walk through:

  • Audio and video applications
  • Compressors and decompressors
  • Debugging, profiling and benchmark
  • Development tools
  • Filesystem and flash utilities
  • Fonts, cursors, icons, sounds and themes
  • Games
  • Graphic libraries and applications (graphic/text)
  • Hardware handling
  • Interpreter languages and scripting
  • Libraries
  • Mail
  • Miscellaneous
  • Networking applications
  • Package managers
  • Real-Time
  • Security
  • Shell and utilities
  • System tools
  • Text editors and viewers

once you selected the packages you like to have included, choose “Save” and confirm as ‘.config’ and then “Exit”.

% cp .config configs/milkv_duo_musl_riscv64_defconfig
% cd ..
% ./build_milkv.sh

and after while, depending on how many packages you selected, you find in out/ folder your new disk image you can copy on the SD card.

Note: buildroot is quite a quirky package, e.g. when you select a package and make a build, later deselect a package, it will still be included – worse, if you commit a clean slate in output/, some packages might not fully build anymore – you have to go back to an earlier state of fewer packages, remake the build, and restart re-selecting new packages.

Postfixing missing .so file

As of 2023/10 v1.0.4 buildroot-2021.05 environment, there seems a problem regarding a missing shared library for some of the compiled apps (like qjs), you can fix this:

% cd /lib
% ln -s ld-musl-riscv64v0p7_xthead.so.1 ld-musl-riscv64.so.1

My BuildRoot Custom Disk Images

disk ImageNotes
milkv-duo-v1.0.4-20231017-spiritdude-64mb_ram-nocam.img
milkv-duo-v1.0.4-20231017-spiritdude-64mb_ram-nocam-fixed.img
– lua, quickjs/qjs, micropython, nano, screen, git, make (no gcc/cc, use tinycc), thttpd2), nginx, lighttpd, php-cgi
– 55MB RAM1) available
– “fixed” image has .so lib-fix included
milkv-duo-v1.0.4-20231016-spiritdude-64mb_ram-nocam.img– lua, quickjs/qjs, micropython, nano, screen, git, make (no gcc/cc, use tinycc), lighttpd (doesn’t work yet out of the box)
– 55MB RAM1) available
  1. apprx. 55MB actual available RAM, no camera support, INO_SIZE=0
  2. thttpd, nginx and lighttpd are all http-server, only use one, see /etc/init.d/ where those are started.

Ethernet Add-On

The easier way is to get proper networking is to add a real ethernet port and properly wire it to the ethernet router. There are several options & sources (2023/09):

Printable Case

I did a small case for the bare board without Ethernet or Extension board to be 3D printed:

Download

As the Milk-V Duo has the same width and depth (X/Y) as the Raspberry Pico but it’s a bit thicker so the some of the existing cases work only partially:

As soon my Ethernet connectors and Extension boards arrived I will provide case variants (2023/10/05).

References