Cellcube Bioreactor Controller
A CellCube runs on media exchanges, and a media exchange done by hand is one person tilting a heavy module with both hands while a third hand is supposed to work a pump. This is the machine that does the holding: a motorised stand that tilts the module to five repeatable positions, a printed pinch-valve panel for the lines, and a pump that dispenses a volume you typed rather than a volume you watched. Everything runs off a Nextion touchscreen with a footswitch under each foot, so both your hands stay on the cube.
The controller firmware isn’t in the folder. What is here is everything around it — the printed mechanics, the acrylic cut files, and seven Nextion HMI projects that are the entire user interface. The display sends plain text over its UART, so what the firmware has to do is fully described by what the HMI sends, and that is what the sections below set out. Until the firmware lands, this is a page you build the hardware from and write the middle of yourself.
What it does
- Tilts the module to five positions —
Left,Level,Rightand two deeperleftpurge/rightpurgeangles — driven by a NEMA 23 stepper through a 50:1 worm reducer onto a 1 in shaft. - A footswitch per direction. The display’s own GPIO reads them, so pressing left or right never takes a hand off the module.
- Dispenses a volume, not a run time. You enter millilitres; the panel converts to motor steps using a calibration factor for the tubing you have on.
- Three pump speeds — slow, medium and fast, fixed at 75,000, 150,000 and 305,175 microsteps per second, with medium selected at power-on.
- Backs the pump off after every dispense by an adjustable number of steps, so the line stops dripping into the vessel once the bar hits 100%.
- Runs the pump in a loop for recirculation, counted in whole revolutions rather than in wall-clock time.
The nextion_10inch_dev_2 project is a second, larger interface for the same
rig, drawn for an 800×480 panel rather than the 480×320 one: a schematic of the
fluid path, a media pump and a circulation pump with independent speed, and
named operations — fill and drain the carboy, fill, drain and transfer the cube,
recirculate, harvest, stop. Its buttons emit Nextion touch events by hand
(printh 65 00 …) under comments like trigger ESP btn event, so that
generation is driven by an ESP32 rather than by the display alone.
Parts
Some links below are Amazon affiliate links. As an Amazon Associate I earn from qualifying purchases — at no extra cost to you. It helps keep these guides free. Outside the US, they should send you to your own Amazon store; if one doesn't, searching the part number there will find it.
- NEMA 23 stepper motor, 1/4 in shaft — the tilt drive. Every printed plate here is cut to the NEMA 23 four-bolt pattern
- NMRV-030 worm gearbox, 50:1 — the reducer the stand turns on, and the reason it holds a tilt with the power off
- NEMA 23 steel motor bracket — the metal alternative to
nema_23_bracket_v2.stl - 6.35 mm to 11 mm shaft sleeve — the motor’s 1/4 in shaft into the RV030’s 11 mm bore.
6.35_to_11mm_bore_adapter.stlis a printed copy of exactly this part; buy the metal one - Roller-lever microswitches, 10-pack — end stops. You need two, and the printed mounts come in left and right hands
- ME-8108 roller limit switches, 3-pack — the bigger CNC-style body, if you would rather have a switch you can see across the room
- Blue threadlocker — on the gearbox and shaft-collar grub screws. A worm drive vibrates
- Nextion Enhanced 3.5 in NX4832K035 with the IO expansion board — Enhanced, not Basic. The HMI files are 480×320 and the footswitches hang off the GPIO header, which only the Enhanced has
- Momentary foot switches ×2 — one per direction
- ESP32 development board — the host that reads the display’s UART and drives the motors. The second-generation HMI names an ESP in its own comments; this is the board to use
- 24 V 5 A supply — the motor rail
- Isolated 24 V to 5 V buck converter — the display and the logic. Do not hang them off the stepper rail directly
- 2020 aluminium extrusion, 2 m — the frame
- 2020 extrusion, 400 mm 4-pack — pre-cut uprights, if you would rather not saw
- 2020 corner brackets, 10-pack and L joint plates
- 2020 drop-in M5 T-nuts — the roll-in kind, so you can add a bracket without pulling the frame apart
- M5 button-head bolt and nut kit
- 1/4 in opaque white acrylic, 12 × 36 in — one sheet cuts the narrow side panel, the fan side, the end cap and the pump profile. The 419 mm main face and the 407 mm wide side do not fit across 12 in, so those two want stock at least 18 in wide
- Masterflex L/S pump head — the HMI is written against one, on a stepper drive that speaks the
/1protocol. Read that section below before you buy - Size 16 platinum-cured peristaltic tubing — the calibration page has L/S 14, 16, 17 and 18 as presets, so buy one of those and it is already in there. This link is an equivalent-spec size 16, not genuine Masterflex; Masterflex sells L/S 16 through lab distributors rather than Amazon. Same bore and wall, so the presets hold, but check your head’s tolerance if you are running it hot
- M3 screw, nut and washer assortment — the pinch valve lids are M3 throughout
- M3 heat-set inserts — for anything you will open more than twice
- 120 mm case fans, 4-pack — every fan opening in the DXFs is a 120 mm spiral grille, six of them across the three side panels. Read the note under Cutting the panels about the bolt spacing before you cut
- 20 AWG silicone hookup wire and female-to-female jumper leads
- PETG filament, 1.75 mm — the pinch valves and anything else on the fluid path
- PLA+ filament, 1.75 mm — everything else
The printed parts
Sixty-four STLs, most of them revisions of the eight things below. What goes on the machine is the highest-numbered version of each.
| Group | Files | What it is |
|---|---|---|
| Tilt drive | nema_23_bracket_v2, servo_nema_adapter_v3, gear_mount_plate, gear_mount_box | Plates drilled to the NEMA 23 four-bolt pattern — 47.1 mm square — with a 25.4 mm centre bore for the shaft |
| Shaft | 6.35_to_11mm_bore_adapter, shaft_adapter_v2, 1in_shaft_adapter, 1in_cap, 0.5in_dowel_mount | Motor to gearbox, gearbox to the 1 in tilt shaft, and end caps |
| End stops | limit_mount, limit_mount_mirror, limit_mount_dual, limit_presser_final, end_stop_mirror | Switch plates in both hands, plus the cams that trip them |
| Readout | tilt_indicator_4, cc_stand_gear_face, dots, handle | A 50.8 mm angle dial, a 96 mm gear face, and a 172 mm handle for turning it by hand |
| Pinch valves | cc_pinch_valve_v5, cc_pinch_lid_v5, double_pinch_top_final_2, double_pinch_bottom_final_2, press_pull_final, valve_push | Single and twin clamps on the media lines. M3 throughout |
| Line management | retainer_clip_v1 / _v2 / _v3 | 25.4 × 6.35 mm strips, three 1/4 in holes each: _v2 is 5 in at 2 in pitch, _v3 is 6 in at 2.5 in, _v1 is 8 in at 3.5 in |
| Enclosures | electronics_box_3, electronics_plate, component_shelf, face_plate, face_plate_bottom, o2_box, o2_box_bottom | The controller box, its backplate, a shelf and a small sensor box |
| Extras | microscope_mount-2, cc_stand_triple_cap, anyconv.com__ut_logo | A 1/4 in-bore mount with two M3 holes at 12 mm centres, caps, and a badge |
anyconv.com__ut_logo.stl exports at unit scale — under a millimetre across.
Scale it in the slicer or you will print a speck.
Print settings
| Setting | Value |
|---|---|
| Layer height | 0.2 mm |
| Walls | 3, and 4 on the drive parts |
| Infill | 25% general, 50% on the brackets, gear mounts and shaft adapters |
| Supports | None on the plates and clips; the tall pinch-valve bodies want them |
| Material | PETG on the fluid path, PLA+ everywhere else |
Print the pinch valves in PETG. A pinch valve is a part that lives under permanent load, and PLA creeps under a constant squeeze — a clamp that has crept is a clamp that leaks, and it does it a week after you stopped watching. The frame plates and the boxes have no such problem, and PLA+ is fine there.
Reorient before you slice. These came out of Fusion with the thin dimension
on Y rather than Z — gear_mount_box exports 159 × 5.5 × 159, electronics_plate
107 × 1 × 150, nema_23_bracket_v2 56 × 10 × 56. Dropped straight onto the bed
they stand on edge. Lay each one flat on its largest face first, which is what
gets the drive plates and the clips through without supports.
Cutting the panels
Six DXF files, all in millimetres and all sized for 1/4 in (6 mm) acrylic. The sizes below are the pieces, not the file extents — three of these files carry construction lines that run off the part, so a naive import looks bigger than what you cut.
| File | Piece | What’s in it |
|---|---|---|
fludics_panel_cut_1 | 419.1 mm square | The main fluidics face: a 166 × 101 mm cutout, an 84 mm square, six 30 mm holes, twelve 7 × 4 mm slots |
fludics_panel_cut_sides | 203 × 443 mm and 407 × 443 mm | Two side panels nested on one layout, two 120 mm fan grilles on each |
fludics_panel_cut_sides_with_fans | 185.9 × 457.2 mm | A third side, two more grilles, and an 83 × 80 mm notch out of the bottom edge |
cell_cube_end_cap | 250 × 250 mm | One spline profile with radiused corner cut-ins, plus a 7.6 mm and a 5.1 mm hole |
pump_profile_dxf | 101.6 × 120.3 mm | The pump’s mounting footprint — twelve 4.9 mm holes, two 14.8 mm, two 3.9 mm |
fix_2 | four 10 mm squares on a 194 × 406 mm grid, plus a 20 × 100 mm slot | Not a panel. A second pass of extra cuts for the 407 mm side |
The fan holes are drawn wrong. Each grille is a 120 mm circle with four 5.6 mm bolt holes on a 109.8 mm square. A 120 mm fan’s holes are 105 mm apart. Move them or cut them as slots — it is a two-minute edit in the DXF and an unrecoverable one in acrylic.
The filenames say fludics, not fluidics. It is a typo that shipped, and it is the reason searching the folder for “fluidics” finds nothing.
Check the scale on import — some laser software silently rescales a DXF — and cut one small profile in scrap before you commit a sheet, because acrylic kerf varies by machine and these panels have press-fit cutouts in them.
The display
Six of the seven HMI projects are for the 480×320 Enhanced panel; the seventh,
nextion_10inch_dev_2, is the 800×480 one described above.
Open cc_stand_nextion_display_footswitch — the unsuffixed original. It is
the only one that tilts anything. The five later revisions dropped the
position page entirely and kept the pump: no Left, no Level, no purge, no
GPIO outputs, no boot init. What they add is a finer calibration factor —
_float replaces the integer cal_fac_int, in hundredths of a millilitre per
revolution, with cal_fac_f in thousandths. That is a third decimal place on
your tubing constant, not a decimal place on the dose; the volume is still
va_vol_int and still steps in whole millilitres. If you want both, the tilt
page merges into a _float project in an afternoon.
GPIO
The panel drives its own IO rather than asking the host to do it. Straight out of the original project:
| Line | What it does |
|---|---|
cfgpio 3,2,0 … cfgpio 7,2,0 | At boot, IO3 to IO7 become push-pull outputs |
cfgpio 3,1,b2 | The pump page rebinds IO3 as an input on button b2 — the right footswitch |
cfgpio 4,1,b1 | And IO4 on b1 — the left footswitch |
pio6 | Media pump run / stop |
pio7 | Media pump direction, cw / ccw |
pio2, pio3 | Loop or bypass — pio3 is held high for 3.5 s to throw the valve, pio2 holds the state |
pio0, pio1 | The right-purge button pulses pio0 high after 100 ms with pio1 low |
n0.val=pio0 | Reads IO0 back, so the start button shows what the pump is actually doing after a page change |
So IO3 and IO4 are an output at power-on and a bound input once the pump page
loads, which is the first thing to check when a pedal stops working: a cfgpio
on the wrong page quietly turns your input back into an output.
IO0, IO1 and IO2 are written but never configured. The cfgpio run only
covers 3 to 7, and a Nextion pin powers up as a pull-up input, so pio0=1 and
pio2=0 land on pins that are still inputs. If the valve and pump lines do
nothing on your build, add cfgpio 0,2,0 through cfgpio 2,2,0 to the boot
code.
What it sends
Everything leaves the display as plain text on the UART, and it is two different languages sharing one wire.
Pump commands go out verbatim in the /1-addressed serial protocol that
AllMotion’s EZ drivers and Lin Engineering’s SilverPak motors speak:
/1V150000R set velocity, run/1P<steps>D<back>R dispense <steps>, retract <back>, run/1gP51200G10R loop: 51,200 steps (one revolution), ten times/1P0R run forward until told to stop — the Prime button/1TR terminate — what releasing Prime sendsPositions go out as one string naming both ends of the move: oldset + " to "
set, so the host readsLevel to rightpurgeand knows the travel without tracking state itself. The page writes all twenty-five from/to pairs out longhand and printsELSEif a combination falls through — which, since all twenty-five are covered, means something has corruptedset.txt.
If your pump does not speak the /1 protocol, the HMI still works — the strings
it prints will simply mean nothing to it. Rewriting them is one page of the
Nextion project, not a redesign, and it is far easier than teaching your firmware
to translate.
Calibration
Volume per revolution depends on your tubing bore and your pump head, so the millilitre figure on screen means nothing until you calibrate. The panel has the routine built in, and it is worth knowing what it actually does.
-
Prime the line. Load the tubing, put about 100 mL of test fluid on the inlet, and hold the Prime button until the line is full.
-
Run the calibration cycle into a tared vessel. It sends
/1gP1280000G2R— 25 revolutions, looped twice, so fifty revolutions in total. -
Enter what came out, in millilitres, and press Finish.
-
The panel stores
pumped_vol × 2as the calibration factor. That number is hundredths of a millilitre per revolution: 80 is 0.8 mL/rev, 380 is 3.8 mL/rev. The four preset buttons are the standard Masterflex L/S sizes — L/S 14 at 0.22, L/S 16 at 0.80, L/S 17 at 2.8 and L/S 18 at 3.8 mL/rev — so if you are running one of those, the calibration is a check rather than a measurement. -
Set the anti-drip steps on the same page. They are added to the forward move and then reversed back out, so the net volume is exactly what you asked for and the last thing the pump does is run backwards — which pulls the drop hanging off the line back up it. If it still drips, raise the number.
Re-run it whenever the tubing changes, and again after a long run. Peristaltic tubing takes a set as it ages and an old line delivers less per turn than a new one — which shows up as a dose that has quietly drifted low, not as anything obviously broken.
Troubleshooting
The stand will not come off a limit. A 50:1 worm is self-locking. That is why the module stays where you put it with the power off, and it is also why nothing back-drives out of an overtravel. The end stops are the only thing between the drive and the frame, so wire and test them before you first move the stand under power, not after.
The progress bar finishes instantly on small doses. Its tick period is
steps ÷ velocity × 10, and every Nextion variable is a 32-bit integer. A 1 mL
dose on 0.8 mL/rev tubing is 64,000 steps, and 64,000 ÷ 150,000 truncates to
zero — so the timer fires as fast as it can and the bar is at 100% before the
pump has finished. The dose is still right; only the bar is lying. It starts
telling the truth once the step count exceeds the velocity, which on the slow
setting means most useful volumes.
The footswitches do nothing. Check you bought the Enhanced panel. A Basic Nextion of the same size runs these HMI files and shows the same screens, and has no GPIO at all.
Nothing pumps after a power cycle. The startup code sends
prints "/1V150000R\r" once, at boot, and selects medium speed. If the pump
missed it, it has no velocity set and will sit there. Touch a speed button to
resend.
Files & downloads
Printable parts
- 0.5in_dowel_mount.stl
- 0.5in_dowel_mount_2.stl
- 1in_cap.stl
- 1in_shaft_adapter.stl
- 6.35_to_11mm_bore_adapter.stl
- anyconv.com__ut_logo.stl
- cc_pinch_lid_v3.stl
- cc_pinch_lid_v4.stl
- cc_pinch_lid_v5.stl
- cc_pinch_valve.stl
- cc_pinch_valve_push.stl
- cc_pinch_valve_v2.stl
- cc_pinch_valve_v3.stl
- cc_pinch_valve_v4.stl
- cc_pinch_valve_v5.stl
- cc_stand_gear_face.stl
- cc_stand_triple_cap.stl
- component_shelf.stl
- dots.stl
- double_pinch_bottom.stl
- double_pinch_bottom_final_2.stl
- double_pinch_top.stl
- double_pinch_top_final_2.stl
- electronics_box.stl
38 more parts
- electronics_box_2.stl
- electronics_box_3.stl
- electronics_plate.stl
- end_stop_mirror.stl
- face_plate.stl
- face_plate_bottom.stl
- gear_mount_box.stl
- gear_mount_plate.stl
- handle.stl
- limit_mount.stl
- limit_mount_dual.stl
- limit_mount_mirror.stl
- limit_presser.stl
- limit_presser_2.stl
- limit_presser_final.stl
- microscope_mount-2.stl
- nema_23_bracket.stl
- nema_23_bracket_v2.stl
- o2_box.stl
- o2_box_bottom.stl
- press_pull_3.stl
- press_pull_4.stl
- press_pull_5.stl
- press_pull_final.stl
- presser_1_revision_a.stl
- presser_1_revision_b.stl
- push_pull_v2.stl
- retainer_clip_v1.stl
- retainer_clip_v2.stl
- retainer_clip_v3.stl
- servo_nema_adapter_v3.stl
- shaft_adapter.stl
- shaft_adapter_v2.stl
- tilt_indicator.stl
- tilt_indicator_2.stl
- tilt_indicator_3.stl
- tilt_indicator_4.stl
- valve_push.stl
Laser-cut files
Vector cut paths. Check the scale against the drawing before you cut — some laser software re-scales an imported DXF on open.