Design and fast prototyping of a milk curd stirrer and heater using Arduino.


This project adhere to the DIY/maker ethos and use a fast prototyping, modular electronics approach.
It also demonstrates that a project can be done even without all the ‘proper’ tools, situations that are encountered
every day in workshops of less developed and fortunate countries, or simply in bad luck situations or when working in a secondary workshop that is not well furnished.
Some tools are non negotiable, but at other times, all it requires is a little inventivity, a bit more skill, patience and the zen spirit.

This is my first true project involving mechanics and electronics. I consider myself with a bit of experience under the belt in electronics, more experience in MCU coding, although
my code is crap before I decide to clean it up and properly factorize (which takes a long time to be put in motion) specially when i code in a rush (that was the case in that project).
I’m sorry for that.
As for mechanical engineering, I am an absolute beginner.
For the thermodynamics aspect, a little research online helped quite a lot.

There are overall a lot of lessons learned and practices confirmed to be important in completing a somewhat complex DIY project.
The more important are, in my case were :

For the idea to proof of concept part, check for state of the art using online resources, specifically video advertisements of similar devices, and see what features you can keep
from them. also check other makers work, and apply the Japanese philosophy of making an already existing product better if you dare.
careful beforehand planning of operation steps. write things.
Ordering of the operations so they don’t conflict each other and make another step impossible because of oversights is the most difficult part, at least for me.
CAD is advised to have a virtual look at the project and is good for post project documentation, although it takes time, and won’t be of real help
if you don’t use machining shops in your project.
May be useful in the future though if you decide to produce more, and also as an advertising and proof of seriousness in your endeavor.
Document your project during execution of the machining, packaging, and all the ‘practical’ work. it will serve you later by giving you a precise
view of the challenges you encountered, so that you can refine the production cycle through continuous improvement. sometimes a picture may be enough.
Video is paramount if you want to make your project public for other DIYers, and advertise it. It is hard to find the time to take pictures or videos when you’re in
the flow doing shop work, because it may break that flow. take it as a break time. making the video useful by being always centered on the work area is the big time cruncher.
In these cases, pictures are a perfect compromise.
Take inspiration of the hackaday like sites to see how experienced makers and hackers document their project.

analyze f***-ups in detail when they happen (root cause analysis, including psychological factors that lead to the mishap) so they don’t happen again
assumptions are the mother of a lot of them.

a great part of the skill required comes from an intuitive feel of Newtonian physics in the conception phase, and dexterity in the shop phase.
Look at a lot of DIY and shop videos to learn skills you don’t have beforehand.

compile the CAD files for parts and from the electronics EDA tool so that your BOM is comprehensive, and order more than required for cheap parts, especially if sourced
from China. sourcing skills are also something you’ll need to work on, and also it is quite fun to browse marketplaces when it is not for mindless consumerism.

Good luck to you !

1) Presentation.

milk curd stirrers are one of the critical devices used in the dairy and cheese industry.
A professional device usually performs these operations :

  • Precise control of heating (heating slope and temperature hold control, multi-step heating)
  • Stirring
  • Curd cutting using a cutting grate.

Some devices implement passive or active cooling using a refrigerant coil.

Professional devices are usually large, several hundred litres devices in the entry level market.
This scale is not suitable for small family farm operations.

This device performs heating control using the PID method (proportional / integral / derivative) and control stirring (on/off).
Curd cutting has to be performed by hand but there are several methods envisioned to expand the prototype into making this process automated.

Vessel used is a 15L Gredil brand stainless steel pan.

1.1 Heating control.

Using a standard 1200W kitchen hotplate that has been wired to be controlled by an SSR relay (fotek brand) and a PWM signal, heating duty cycle can be defined by the
PID algorithm.

  • Heating phase uses open loop control with inputted estimations of thermodynamic parameters (vessel geometry, external temperature, quantity of milk, heat capacity & transfer terms)
  • Heat transfer terms take into account conductive and convective effects.

Due to the large mass of the heating plate, thermal inertia is quite high and has to be compensated with a large derivative parameter in the PID controller loop.
Ceramic is a good choice, induction plates even better, although there are few single plate portable models for these technologies. Price tag is higher.

Closed loop control kicks-in at a defined set point before reaching temperature hold plateau to minimize temperature overshoot.

A thermoter probe well is present and secured to the base plate to perform temperature measurement as close as possible to the project center of mass of the liquid to be heated.

1.2 Stirring.

A 28W low rpm single phase AC motor, geared for 15 rpm, provides enough torque to enable consistent stirring. The motor is coupled to a 8mm shaft, ending with two perforated 12*15 cm , 2mm wide 308 stainless steel plates, with each having 5 33mm diameter holes. the shaft is terminated on its contact point on the steel pan with a conical taper to reduce friction between the two. The shaft is secured by a standard 8mm flanged bearing to the base plate crossbar.

Not included in this prototype, but highly recommended is to provide a rotor lock detection mechanism by monitoring the current drawn by the motor, and disabling it in case of overcurrent. Monitoring the motor temperature is also recommended based on the provided datasheet parameters.

1.3 Protection and conformity devices.

The devices uses two DIN circuit breaker inside a 2 breaker box. one 10A for the hot plate power, one 2A for motor and Arduino power. the circuit breaker box is secured by screws to mounting struts across the two slotted aluminum profiles, earthing of the whole metallic chassis of the device is provided at this point.
Note that fast period PWM induces switching noise on the mains AC. An adequate line filter for a 1200W hot plate is recommended.

1.4 The controller.

An Arduino Mega 2560 with an LCD 1602 keypad shield is used to perform the following operations :

-Motor on-off control for stirring and hot plate PWM control
-Temperature measurement
-Liquid quantity measurement using a laser distance measurement module.
-User interface (LCD 1602 keypad shield) to input program parameters and program selection

Liquid quantity measurement is tricky and one need to carefully plan the placement of the module based on the datasheet recommendations.
Specifically, the hole through through the baseplate and electronics enclosure has to be wide enough not to disturb the laser cone, and it is also very important that the PMMA protection window is mounted as close as possible to the module, ideally in contact with it. A distance larger than a couple of mm is sufficient to induce large crosstalk, making the measurements lose precision. at 10mm distance, measure is no more effective. The placement of the module on the horizontal plane also has to be made distant enough to the vessel walls for the same reasons. Two more constraints : the protection window has to be thin and provide adequate IR transmittance. PMMA is a good choice. Also, water vapor condensation is a problem for hot running measurements. A hydrophobic treatment of the PMMA window is therefore required.
On the other hand, milk is quite reflective and allows rather precise measurement to a 5% to 10% error margin.
This module requires quite a lengthy process of calibration : distance offset, measurement of materials with different reflectance, and crosstalk calibration.

A simpler approach would simply to ask the user to input the quantity of milk present inside the vessel.

2. Bill of Materials.

  • Aluminum strip 30mm x 2mm (for struts between the aluminum profiles, and to secure the breaker box) 50 cm
  • slotted 20×20 aluminum profile, length depending on the vessel depth, and adaptability for varying depth vessels qty 1
  • 68KTYZ motor, 10 rpm qty 1
  • 68KTYZ right angle mounting bracket qty 1
  • 8mm 303 stainless steel shaft, length depending on the vessel depth, and possibility of using vessels of varying depths qty 1
  • 8mm ball bearing with mounting holes, model KFL08 qty 1
  • aluminum shaft D19L25 coupling nut 7mm to 8mm qty 1
  • stainless steel 309 plate, 12x30cm 2mm width. This depends on the vessel geometry. used for both paddle wings qty 1
  • slotted profile 20×20 nuts, to secure struts, motor bracket, and plate to profile mounting brackets qty 12
  • aluminum profile 20×20 90° corner brackets, to secure aluminum profile to baseplate qty 4
  • ABS electronics enclosure 140x110x35mm Gainta Brand G738 qty 1
  • thermometer probe on cable DS18B20 qty 1
  • thermometer well, 20 cm length, 11mm width qty 1 2 DIN breaker wall mounting box (PawBol C.2030) qty 1 Arduino mega 2560 qty 1
  • perfboard (at least 140×110 mm) qty 1
  • LCD 1602 Arduino keypad shield qty 1
  • laser time of flight (distance measurement) module TOF050C (VL6180X based) qty 1
  • standard 5V relay module qty 1
  • Fotek SSR40DA solid state relay (5V DC control) qty 1
  • Hi-Link 5V power supply module, 3W qty 1
  • 275V varistor qty 1
  • 4.7K resistor (for temperature probe) qty 1
  • Silicon wire hookup cable, 22AWG qty 1
  • M2,M4 and M5 screws qty 1
  • IEC C14 male cable connectors qty 2
  • IEC C14 female connector qty 1
  • schuko cable C14 female terminated qty 1
  • 1.5mm2 conductors, blue, brown and green/yellow, 1m
  • cable relief and clamp nuts (for cables entering the electronics enclosure) qty 3
  • 1200W hotplate qty 1
  • 15L stainless steel vat, Gredil brand qty 1
  • U bolts. geometry will depend on the stainless steel vat handles position qty 2
  • butterfly bolts to secure U bolts to the baseplate qty 4

3. Tools needed

  • bench drill and HSS metal drills up to 12mm diameter
  • angle grinder with cutting disk and grinding disks
  • drilling and cutting oil
  • threading taps
  • 33mm hole saw for metal
  • stainless steel 2mm stick welding electrodes
  • stick welding inverter
  • perforated welding table or at least two vices, with one vice with XY axis regulation
  • spacers of varying widths to assist in vertical position of the paddle during welding
  • piece of copper to use as heat sink for stainless steel welding (to prevent warping)
  • a dremel with 28mm cutting disks and small abrasive (diamond) milling bits with various forms
  • soldering iron
  • multimeter
  • power screwdriver and angle grinder (to make the conical taper at the end of the shaft) or better, a lathe.
  • caliper and measuring tools
  • sharpie pen

4 Making the prototype

4.1 preparation of the baseplate

clean it and remove any protective film. mark the center with a sharpie. this will be the hole for the shaft.
drill the shaft hole some 20% larger so that there is leeway in final positioning of the shaft and bearing assembly

4.2 mounting the aluminum 20×20 profile

cut your aluminum profile in two parts (if applicable). the original extremities will be positioned over the baseplate,
since it is not certain that your cut angle is perfectly perpendicular if done with an angle grinder or a hand saw.

4.3 mounting the motor 90° bracket

place all T slot nuts inside the profile and position them for screwing the bracket. ensure that the base of the motor bracket is
parallel to the baseplate during test positioning. Screw the 68KTYZ motor to the bracket, install the shaft coupler, pass the shaft through
the bearing, position once again the assembly to the baseplate passing the shaft through the baseplate hole. test for shaft perpendicularity
using a protractor and you eyeballing abilities, using a protractor oriented in X then Y axis. beware of parallax. Note that this is not critical
since shaft coupling nuts have a helical groove to allow precession of the shaft, but it’s better to have the habit to do the right thing.

mark with a sharpie the outline of the corner brackets of the profile/motor assembly and the center of the holes of the brackets and shaft.
Check that the shaft does not contact the baseplate hole.
drill the holes for the baseplate to profile L brackets, again a bit larger so there is leeway for final positioning and screwing. once drilled secure the baseplate to the profile/motor assembly while checking that the assembly sits so that the shaft is perpendicular to the baseplate, again use protractor or a fancy laser. also at this point position the baseplate so the
shaft comes into contact at the center of the vessel bottom.

4.4 U-bolt baseplate to vessel fasteners

At this point it’s time to prepare the u-bolt holes that will be used to secure the whole device to the vessel using its handles.
this part is critical.

mark the contact point of the vessel lid to the baseplates, 4 points in total. will be useful if it moves inadvertently.

Since there is no contact except at the lid of the vessel, you’ll need to use a square ruler with a large contact area to project the handle of the vessel to the baseplate, offset to the centerline (axial) of the handle cylinder, and add offset of half the distance center to center of the u-bolt, mark the holes and drill. better drill to exact u-bolt bolt body dimensions, so there is no room for it wiggle if the butterfly bolts come loose during operation.

once again unscrew the motor/profile assembly and remove the shaft, and drill the u-bolt holes.
after that repeat the positioning steps above with the motor/profile assembly on the baseplate secured, and the baseplate secured to the vessel using u-bolts and check that
the shaft once again contacts the center of the vessel.

4.5 layout of thermometer well and electronics enclosure

test layout of the remaining components all at the same time :
the thermometer well, the bearing orientation, and the electronics enclosure.

there is a constraint on the VL6180X laser distance sensor (the laser cone of avoidance). some trigonometry will be helpful to define the minimum diameter of the electronics
enclosure window and the baseplate window. verify that the cone divergence does not intersect the wall of the vessel.
you can also skip this part because it is quite tricky and go for static liquid quantity input by the user.

4.6 securing the ball bearing to the baseplate

mark the holes for the ball bearing while the motor/profile assembly is correctly positioned.
remove the assembly again
secure the ball bearing. position and secure the motor profile assembly again, check that the shaft does not contact the baseplate.

4.7 thermometer well installation.

you can either place the thermometer well in the hole, it will rest on its flange, (quick and dirty) or glue a NFT threaded adapter to the baseplate so you can screw in the well and it does not move. you’ll have to carve out a slot in the paddles so that the well does not collide with the rotating paddles

4.8 define the layout for the components inside the electronics enclosure

at this point you should have the positioning constraint for the distance laser module on the XY axis.
non soldered components are the SSR and motor relay : they will be freestanding inside the enclosure and secured by the perfboard on their sides
the Arduino and LCD shield assembly will be secured to the top part of the enclosure.

Drill two holes in the perfboard at least, diagonally, you’ll use the same screws to secure the perfboard to the enclosure and to the baseplate.

position and mark baseplate with a sharpie through the bottom cover holes.
Drill the baseplate holes. baseplate holes maybe threaded or not and you’ll use bolts. it’s up to you. I tapped threads and was lucky. beware that threads in aluminum are soft compared to steel. threading is less robust but a little more elegant than bolting.
test mount bottom cover and perfboard to the baseplate.

Carve out the perfboard for SSR, relay and Arduino footprints. This step is required if you use the enclosure specified (Gainta) since there is not enough space on the Z axis for these components. if you find a better enclosure on the market, lucky you ! or just 3D print one.

The good side of the tightness of placing was that the perfboard maintained the two relays in place. better carve out less so that you finish with a
mill bit on the dremel and obtain a tight grip of the perfboard to the components.

also note that I had to chop parts of the fotek SSR plastic near the connecting terminals to connect the washer type electrical connectors.

of course, before carving out, you have to make sure that the remaining components fit on the little perfboard you have left : that is, the varistor on the AC side, power module, and the thermometer module resistors, maybe an electrolytic capacitor on the PSU output.

4.9 remaining holes on the bottom part of the enclosure

the bottom part of the enclosure will have three additional holes for the wire ports, two at the side closer to the center of the device :
one for the hot plate electrical wiring input to the enclosure (phase, neutral, and ground) the other port for the stirring motor power output (AC), and the MCU + motor power input (AC)
the distal hole from the center is for power output going to the plate.

If you have a precision bench drill use it for the holes in the enclosure, and finish with a dremel when required. the port holes are better milled with the dremel, since there is usually no
clearance to drill them with a precision bench drill, unless your setup allow to do so.

so, to sum up, the enclosure bottom part has these holes :
2 mounting holes to secure to baseplate and perfboard
3 holes for cable routing

finally, one of the enclosure plastic insert sides should have a hole for the arduino USB connector.

4.10 Soldering the components to the perfboard

First, A note about the VL6180X module

if you decide to implement it, remember that the PMMA window has to :

  • make the enclosure airtight and watertight
  • be in contact with the module itself to lower crosstalk to a minimum
  • be fog treated
  • be heat resistant. better measure the temperature at that point at the maximum operating temperature and liquid quantity of the device. 92°C liquid temperature is used for milk sterilization

that means that the Z axis section for the laser module has to be layered as this (going downwards) :

  • perfboard
  • laser module (using 90° header pins)
  • pmma window
  • enclosure bottom cover
  • baseplate

the PMMA window should be way larger than the hole so that the epoxy gluing step does not smear glue down the hole. use the least amount of glue over a rectangular path,
so the enclosure stays airtight.

Remember step 5 and check underside that the VL6180X chip is well inside its positioning margin so that the laser cone does not interfere with any part. re-mill if needed.
better perform calibration before gluing the window. the process is outside of the scope but there are well documented in the datasheet. You’ll have to write into quite a lot of registers.
on the VL6180X, check my code (will comment it better soon)

Also the module will be soldered on the perfboard bottom layer. a dual layer perfboard is thus required.
Finally, the perfboard may not be in contact with the bottom of the enclosure because of the module, in the case you do not use the provided mounting holes on the enclosure, as they provide some clearance from the bottom cover. if I remember well, I could not use them because of the crowded layout. This may lead to the bending of the perfboard, and axial force on the perfboard/cover/baseplate screws. if you manage to add plastic standoffs washers over the screws between the perfboard and the bottom cover, it will give a better result.

4.11 wire soldering to the perfboard (perfboard to perfboard and perfboard to freestanding)

pass the DS18B20 wires inside the wire restrain port and solder them to the perfboard.
solder the AC carrying wires leads (low amperage, these will connect to the 2A breaker and supply power to the module and motor) to the varistor and pass them through the port, later they will be bundled with the hotplate carrying wires. this could lead however to stronger switching noise coupling from the PWM operation to the AC power lines to the controller and motor.
In my case I did not use any line filter in the enclosure since there was no room left, but the hi-link module probably handled this well as controller operation is stable.

solder the hi-link power supply, and all interconnect wires.

this project feeds 5V to the Arduino through the 5V pin, so it bypasses the regulator, thus an electrolytic capacitor is advised to smooth DC when the motor relays kick in.
hi-link power supplies are good if you get the real thing, mine had to be opened, and a soldered wire was loose so it didn ot supply 5V. either i did not have luck or it was
counterfeit crap. lucky that it was just a wire issue. Always test ‘complex’ modules and components on breadboard first ! and if you can, order more than required since they’re cheap
and take time to get to you. Always think about logistics in any project !

One more advice :
I made the rookie mistake to use single stranded breadboard wires. don’t do that, they are fragile as f*** and break on torsion. used proper multistranded hookup wire.

label all the hookup wires on the perfboard side with a Dymo.
secure for the final time (hopefully) the perfboard, bottom cover, and baseplate together.
put the Fotek SSR and the relay in their designated perfboard placeholders
additional internal wiring using DuPont connectors are 5V,GND,relay logic for the motor relay, SSR relay logic and SSR DC ground.
the Chinese 5V relay modules provide male header pins. I advise not to directly solder hookup wires since that will make any servicing difficult.
Fotek SSR use large screw terminals the like used on overcurrent protection breakers, you’ll have to use washer type electrical connectors.

4.12 hot plate power power wiring

Pull neutral and ground 1.5mm2 wire through the enclosure through one of the ports on one side, across the enclosure, to the exit port.
split the same length of brown phase wire in two and solder a washer type electrical connector on one end of each wire, connect them to the AC part of the Fotek SSR.
the connectors will connect one one side to the dual pole 10A breaker on one side, and to the female (important that you use Schuko IEC C14 female connector here to prevent electrocution from the potentially energized prongs when it is disconnected from the plate) Schuko connector that will connect to the male ended Schuko connector cable going to the hot plate.

4.13 prepare the Arduino and the LCD 1602 shield

unfortunately the LCD 1602 shield pin headers come into contact with the top cover in my configuration. the easiest way for me was to saw them off with the dremel.
take care to not let drop the shavings mindlessly.

next solder hookup wires to the LCD shield for :

  • DS18B20 data
  • SSR relay logic
  • motor relay logic
  • 5V
  • GND

And if you use the I2C VL6180X laser distance module, add these cable leads (that’s because the LCD1602 keypad does not route the SDA/SCL through its pins, what a shame) :

  • SDA
  • SCL

4.14 prepare the top part of the enclosure

The fact that the LCD keypad shield plus Arduino have to be secured to the top part of the cover introduces a severe constraint for wiring the whole project.
That’s all Aliexpress had to offer in 2022.
This whole part will have to be rethought in any next iteration, with ideally an LCD keypad solution with ribbon cables and board to board JST connectors, and a PCB plus embedded Arduino on the bottom part, and a larger enclosure on the Z axis.

The top part will have a carved out rectangle for the LCD display, 4 holes to secure the Arduino/LCD and 5 holes for the keypad buttons,
Mill an opening on the enclosure top cover for the LCD1602 display and the five buttons keypad, with the dremel using cutting disks for the opening and milling bits for the keypad. you’ll also need mounting holes, beware when milling there is very little clearance between the holes and the display opening. go slow.

finally connect the shield to the Arduino and mount the Arduino with the shield to the top cover. screws and nuts required.

4.15 connecting the hookup wires leads from the Arduino to the wire leads going to the perfboard, the SSR, and the relay.

put a heat shrink tubing section on the cable solder all hookup wires from the perfboard / relays to the corresponding hookup wires of the Arduino/LCD, place the heat shrink tube over the solder and apply heat.
Of course, for this part you can go for ease of maintenance and just use DuPont male to female connectors cables to connect the bottom cover components to the Arduino, I just trust them less in the long term.
This part is a bit cumbersome since it has to be done with the enclosure partially closed, just to expose the wires.

4.16 closing the enclosure

once everything is in place close the enclosure slowly, checking not to pinch any cable, and screw and tighten the top cover.

4.17 circuit breaker box installation.

While one side of the parallel two 20×20 profiles is used for the motor and its bracket,
the other side will be populated by the electrical breaker box. I used a model for two DIN breakers, since I’m in Europe.
the box will be mounted on the two aluminum strips that serve also as struts. I made two diagonal holes to secure the box, one of these holes will also be used to ground
the device chassis, by the means of a washer electrical connector. grounding for the hot plate has to be provided, so you will need a 3P Wago to extend the earth or use two washer
connectors on the grounding point.

functional tests of the completed device will be covered in another post.

4.18 shaft conical taper

use a lathe or if you haven’t any, make the shaft spin on a power screwdriver, with support along the axis with a ball bearing secured on a vice.
have someone assist you or clamp the power driver trigger so it spins without assistance. once the shaft spins, make the conical taper using the angle grinder.

4.19 paddle cutting and perforation

using a metal hole saw, drill the holes on each paddle, using the same layout for both paddles. check the CAD model.
use plenty of drilling oil and stop drilling and let the saw cool down if smoke appears.

4.20 paddle welding

This part is tricky, because the fixture is a bit complicated to make when you don’t have the proper tools. Best is to have a true welding table with different types of clamps. If you have no welding experience, better give the work to a welding shop.

Without adequate tools you can make a somewhat ok-ish fixture using one vice to secure the shaft, and another 2 axis vice. you will need some spacers placed under the vice to adjust the them on the vertical axis relative to one another, so that the paddle width centerline is aligned diametrically to the shaft.
Then adjust XY axis on the vice that hold the paddle, just above the conical taper. better to have some margin if the paddle ends misplaced so that it does not contact
the vessel when you’ll test it, it will spare you some grinding. in my case, i ended with a max. 5mm margin between the paddles and the vessel, on the base and on the side.

If you’re a beginner like me, it should work (not ruining the parts) but aesthetically it could be crap.
I made a 45° bevel on both sides of the edge to be welded to create a groove to improve the weld robustness.
not sure if it really helped though, better ask a professional.
I used 2.8mm 308 stainless steel rods at 80A, I think it is a bit too hot, but I don’t know if my crap welder is calibrated for amp measurement.
The most important thing is a precise alignment of the paddle to the shaft and to use one or two copper heat sinks contacting both the paddle and the shaft close to the
weld area, underside of course. even like that, the shaft may warp. do not be too alarmed if it does though, because when welding the reverse side, the warping will revert, but because of this you could have trouble to make precise contact to the copper heat sink though.
repeat for the other paddle.

I learned that stainless steel welds may need passivation or electropolishing after welding to be fully corrosion resistant. there are DIY electropolishing methods involving strong acids like phosphoric acid and current, passed through some mineral fiber soaked in acid, and connected to an electrode. check Youtube.

4.21 final paddle assembly rotation tests

make free wheel shaft/paddles spinning tests with the motor coupling Allen nuts loose. it should not contact the vessel sides or bottom. A final test will be required with the motor on.

upload a test firmware to the Arduino and test the relays. at this point the motor relay is the focus of our attention, make the paddle spin, and if it contacts the vessel, grind the paddles at the point of contact until they no longer do.

4.22 breaker box to enclosure wiring connection, enclosure to motor wiring connection, enclosure to hotplate wiring connection, breaker box to main AC input connection

To sum up, there are three bundle of cables : phase, neutral, protective earth for the hot plate, passing through the enclosure and going to the 10A breaker, plus phase and neutral going to the 2A breaker. (since there is no line filter the protective earth does not need to be routed to the controller enclosure) these will provide power to the controller and to the motor through the relay.

and finally a bundle of cables exiting the other side is going from the enclosure to the hotplate.

the lone remaining cable is for the DS18B20 probe and will be shoved down the thermometer well.

use shrink tubing over each bundle and hot air gun blast it. then pass the leads down the breaker box hole on the top and connect them to the respective breakers.
do the same for the enclosure to hotplate, with a Schuko IEC C14 female termination. since the hotplate has to be easily disconnect from the rest, that’s why we terminate we a Schuko. keep this cable and Schuko connector length short.
Screw Schuko connectors are a bit tricky to fasten properly, in my opinion. If I had to do it again I would sacrifice a power cable to use the molded connectors.
repeat the process for the main power AC input, use a male IEC C14 Schuko this time. this cable enters the breaker box through the bottom. keep this cable and Schuko connector length short.

5. Additional resources.

5.1 Firmware

Firmware is available at

Alpha Version.

5.2 CAD model

Take the model with a grain of salt. it’s mainly for illustration purposes and I used available GrabCAD and Mc Master Carr parts models, some of them do not match the components I’ll used.
the model, as for the date of this post, lacks drilling information, the breaker box is also not modelled.
If one day we have an AI managed database of all market available solid parts, using 3D reconstruction, that will make modelling easier, but human modelers will be rendered obsolete.
AI will drive one of the largest work shifts in human history, that’s for sure.

CAD model uses FreeCAD v 0.2 the model is available in :

5.3 Schematics

Also available at (authoritative resources are uploaded on GitHub) :

5.4 Pictures

I hope to keep this project alive so It may serve the makers community well.

3 Phase Modbus Smart Power meter with relay actions and power line communication telemetry

# PMTR_001_SRV_001 V0.22


PMTR (Power Meter, Telemetry & Relay) is an open source project that allows precise monitoring of mains parameters (voltage, current, active power, energy, power factor and frequency) and allows to act on external relays based on formula triggers (ex: overcurrent or overvoltage)It is a client/server project with a Web interface for data display, logging and device configuration.It uses power line communication and the industrial proved Modbus protocol to transfer data between the client and servers; no need to have ethernet or wifi at your main power panel.
It is modular and robust in approach, using off the shelf components available on Aliexpress or other vendor sites to ease replacement of parts if need arise, and thus lower TCO for many years.
The server(s), (PMTR_001_SRV) is an Arduino based device that performs the power measurements and relay actions. Its configuration is pushed by a client.
The client(s) (PMTR_001_CLI) is a Raspberry Pi device that grabs power telemetry and pushes configuration and time synchronisation to the server(s) device. As stated above, it does not rely on an Ethernet connection, just a Power line communication based on the ES-1642 NC module (also available on Aliexpress)
Relay actions are used mainly to protect your equipment (for industrial settings) and control energy costs (ex: hotel industry, power charging for EV), It will also allow to cut/enable power at specific time of the day.
This project, using the modbus protocol, will allow several servers. thus you can monitor individual circuit branchings on power subpanels.
Two flavour of servers are being developped : A single phase meter, and a 3 phase meter. they are rugged using fuses and varistors for overvoltage protection.Metering is based on modified PZEM-004t v3.0 modules that can measure line to line voltages up to 437V AC.
For more information on the client, check:

For more code information on the modbus server, check:

Current State of the project.

A single phase proof of concept with a raspberry pi client and a single phase power meter was done and shows good results. The server code integrate formula based actions, and time synchronization. A basic web chart of power parameters and logging into a MariaDB database is part of this POC.

What needs to be done in priority as of November 2022:

  • Finish the 3 phase prototype server, currently designed with Easy-EDA pro.
  • Add ADG333 switching of serial line to query each PZEM-004t V3 modules. (done sequential querying with two modules, ok)
  • Add code for DS3231 time keeping.
  • Harden and test formula actions where conflicting pins are used with different actions.
  • Make the raspberry pi (wifi, serial) configuration seamless
  • Expand the raspberry pi client to query several servers with auto-detection on the PLC bus
  • Add a MQTT layer on the raspberry pi for upstream telemetry reporting
  • keep in touch with PeaceFair, the maker of the PZEM-004t V3.0 to make a hardened 400V module (there are still some issues regarding with high voltage tolerance)



typical use case for 3 phase delta-wired measurement:

This use case shows the installation of the PMTR_001_SRV for monitoring voltage and current drawn by a 3 phase induction motor, and all remaining power parameters.

Note that since the motor is a 3 phase delta wired device, PMTR_001_SRV has to be setup in the following way :- Hardware method : By placing measurement jumpers inside the device for line voltage measurement (L1,L2),(L2,L3)(L3,L1)OR- Software method : By specifying a multiplicative correction factor : sqrt(3) for power and energy measurements inside the PMTR web interface. this factor will be uploaded to the PMTR_001_SRV device
The setup contains a single digital pin operating a 5V relay (NO) that provides in turn power to a contactor (NO) that power ups (or down) the induction motor.
It is then trivial to use logic to specify a relay action that evaluates and power ups or down the motor upon specific power conditions. More on relay actions below.
Also note that the PMTR_001_SRV and PMTR_001_CLI are connected on the same line (L1) for PLC communication to occur.

Other installation best practices

PMTR_001_SRV Contains a ES-1642 NC PLC modem (Power Line Communication). No Line filter should be present between the server and the client, the client would be unable to communicate with the server !L1, L2, L3 current coils should be placed on the current carrying wires of the metered installation, not on the wires supplying energy to the meter PMTR_001_SRV can operate over IT, TT, TN-C TN-CS earthing schemes.Being a non metallic chassis, PMTR_001_SRV does not require to be grounded.PMTR_001_SRV contains ceramic fuse protection and varistor overvoltage protection.PMTR_001_SRV will be packaged inside a standar DIN Rail plastic enclosure for ease of installation inside a power distribution main panel.
Check the HW folder of this project for the terminal pin references.

Installation Steps

  • 0) Disconnect power to the facility or to the VFD for safe installation of the current coils.
  • 1) Setup Modbus Address using the 8 position DIP switch to any value between 1 and 254. 0 is a broadcast address. 255 is a bridge address. Do not use these on the PMTR_001_SRV2) Set the 3 jumpers to LN metering – default – (line to neutral) or LL metering (line to line) Usually LN is preferred if the metering is on a standard european consumer network. LL metering is usually reserved for Variable Frequency Drive metering. In that case, skip step 4.
  • 2) After each phase is disconnected from the terminals, insert the current coil over the wire and reattach to the input and output terminals. repeat for each phase.
  • 3) Connect CT- and CT+ wires to the appropriate PMTR-001-SRV terminals. Repeat for each phase Then,
  • 4) Connect neutral wire first to the PMTR_001_SRV
  • 5) You can re-establish power inside the facility now or power the VFD
  • 6) then connect L1 L2 L3. Beware, lines are powered on, use an electrician screwdriver !!!!When connecting L1, the unit should power up, unless on a VFD where you need at least two phases. You can test that all DC power supplies are providing backup power by connecting them individually. PMTR_001_SRV should work with either L1, L2 or L3 connected in LN metering.
  • 7) if a backup / battery supply is available you can connect it to AUX12VDC to the positive terminal and GND to the negative terminal
  • 8) Tie the SEND terminal to any L1 L2 or L3 phase, now the Rasperry Pi (PMTR_001_CLI) can be connected to the same phase and neutral as the PMTR_001_SRV and it should set the time up
  • repeat the same actions 1 to 8 on any other PMTR_001_SRV to install on the network. use the same phase line for all the PMTR_001_SRV that you install
  • PMTR_001_SRV can supply up to 0.8A at 12V over the 12VDC_OUT terminal, electromechanical relays can be powered by 12V IF the 5V relay action pin is recognized as HIGH by the relay.anyway, PMTR_001_SRV can supply up to 1.7A at 5V over the 5VDC_OUT terminal, for electromechanical relays. nb: The relay action pins cannot supply more than 40mA current, use is limited only to SSR relays !!
  • Proceed to PMTR_001_CLI Readme to finish setup.

Modified PZEM-004t v3.0 information

The original PZEM-004t v3.0 can measure up to 267V RMS.
Here, The metering is done through a modified PZEM-004t v3 for up to European phase to phase voltage tolerance :up to 427V RMS with 0.2V resolution. Compared to the non-modified version, This ALSO allows phase to phase metering, and phase to neutral in case of neutral fault.Power, Energy, power factor resolution are doubled due to the fact that their computing involve instantaneous voltage measurement which is affected by the PZEM modification.
Otherwise, the floors, ceilings of the remaining parameters conforms to the PZEM-004T v3.0 datasheet of this day.It is expected that frequency measurement is not affected by the PZEM-004t v3.0 HV modification.For power factor, leading or lagging info is not provided.
PMTR_001_SRV performs instantaneous measurement as fast as the 9600 bps serial interface allows, which is a little less than 100 per second (all parameters read)When performing 3 phase metering the PMTR_001_SRV performs serial port switching (using an ADG333A Quad SPDT switch) So it shares a single serial port on the Arduino for 3 PZEM-004T modules.this lowers the read rate to less than 33 per second.
an average value over 1 second of these 33 samples is stored as the “1 second average”On the basis of this 1 second average, then a moving average from 2 to maximum of 60 seconds is saved.
The device has 5 memory slots for Moving Average information of all parameters (except energy)For instance, We could store the Moving Average over 5, 10, 20, 30, 60 seconds.
The incentive behind moving average calculation is to apply low pass filtering and filter any spurious spike that would make an inappropriate relay action at the time.

Relay Actions

A ‘relay action’ is a set of the following parameters :

  • An expression : using tokens representing PMTR_001_SRV parameters, and a set of inequalities and logic.- A moving average to use for expression evaluation (except energy which is never averaged)
  • A dry run bit (Yes/No), which tells whether to perform or not the relay action, while still logging all action behaviour
  • A recovery period during which the expression must keep evaluating to false before reverting relay action. This hysteresis is implemented for protecting devices which should not be turned on/off too frequently.
  • A HIGH or LOW state to command the relay when expression evaluates to true. (on false evaluation the state commanded will the be opposite ) nb : This evaluation from memory is the first action to be executed at boot up.
  • A list of pins on which DIGITALWRITE Will be performed.It is recommended that each action activates a separate set of pins, otherwise the last evaluated actions will have precedence and overwrite the status of pins.

There are 5 actions slots.

Examples of valid expressions :

  • (L1U > 253) || (L2U > 253)) || (L3U > 253)
  • LII > 16
  • LIp > 1200
  • L2e > 10
  • (L1f < 49.1) && (L2f < 49.1) && (L3f < 49.1)

Ex : L1U is phase to neutral L1 RMS voltage, L1I is phase to neutral RMS current, L2f is L2 frequency.Units use are SI, excepts for energy which is in kWh
First case would be the detection of a voltage swell over 253V over L1,L2 and L3 at the same time.The second a current of more than 16A on L1Next an active power of more than 1200W on L1Next, A total energy consumed of more than 10kW over L2. Finally a frequency sag of 49.1 Hz over any of the phases.

Neutral fault detection

Broken neutral (floating) fault in three phase systems can have disastrous consequences by essentially rewiring the loads in a delta fashion.

Depending on the location of the fault and earthing arrangements, voltage imbalances arise, and the neutral point is shifted and may be subjected to voltages relative to earth that may be unsafe.

A sucessful neutral fault detection without false positives is not trivial based on a limited number of voltage readings (three power meters only), absence of phase angle information, and absence of a neutral to separate ground electrode voltage measurement

However, using magnitudes of voltages only and their variation in the time domain according to load variation, we can tentatively propose an lightweight algorithm suitable for MCUs to detect such occurences.

Keeping in mind that if the load are reasonably balanced the fault will be harder to detect, until a disturbance in the balance of loads will unbalance voltages.

Generally speaking, a broken neutral condition will translate in an increase of one phase voltage, while the other two will decrease with reference to the nominal value, or one will decrease while the other two will increase.

A lightweight pre-check for discriminating between single phase power disturbances and neutral fault would be to check for any voltage exceeding a 10% nominal value deviation, and then proceed to check remaining voltages for disturbances.

A valuable tool for analysis of neutral fault conditions is the three phase voltage triangle. By using basic trigonometric identities of the equilateral triangle, we can characterize the least possible deviation in absolute values of the other two voltages assuming epsilon is the deviation factor from nominal value for the upmost voltage.

$$ \epsilon = 1.1 $$

$$ V_{p} = 230V $$ is the nominal phase RMS voltage

$${V_p}^{‘} = \frac{V_p \sqrt{4+12\epsilon^2}}{4\epsilon} $$ is the least possible deviation from nominal values for the two remaining voltages.

Thus we can write : $$ V_pdev >= |V_p – {V_p}^{‘}| $$

So, in terms of ratio we have $$ \frac{V_p^{‘}}{V_p} $$

The algorithm has to check that the remaining two voltages is higher or equal to that ratio for the first voltage and lower or equal to that ratio for the second voltage. Once this pre-characterization is done, a second part, more computationally intensive part of the algorithm can kick-in, to check that the sum angle derived from magnitude of the phase voltages is still close to 360° degrees, while analyzing voltages changes in the time domain.

Phase angles can be derived from magnitudes using sine and cosine trigonometric identities of the triangle. voltages fluctuations on the three phases with time that keep their sum angle close to 360° can be assigned a score for each angle sum characterization. With a value close to 360° giving a positive score, and a value diverging from 360° giving a negative score.

A sum of absolute values differences between discrete time samples of the three voltages can assign a weight to the characterization. This characterization puts into evidence the variation of load impedance in a broken neutral condition, and gives more credence to the computation if the angle sum keeps getting close to 360°

Once a positive score compounded by the previous weight reach a threshold, a broken neutral condition can be raised.

There are caveats however. a robust algorithm must assess the stability of the line voltages over time, preferably over a long term period. Using only three power line meters, a common occurence of unstable line voltages will degrade significantly the predictive abilities of the algorithm.

Hardware in-depth information

AC protection features

– 20mm x 5mm 0.1 A ceramic fuses, fast blow 250V, this for each phase L1 L2 L3 and neutral N- 14D481K varistors between each phase and neutral, and for protection of the PLC module.

AC/DC conversion

Power is provided by YHT4S12V/10W modules AC380V tolerant.the three modules are connnected line to neutral and should be tolerant to neutral fault and up to 2 phase faults.

DC Bus

Matched Schottky diodes are used to improve power sharing betweeen the modules at 11.5Va single LM2596 dc step down module provides 5V to the DC bus


ES-1642NC uses a separate hardware serial port for modbus communication over one phase and neutral. the serial line speed is 9600 bps. the PLC speed is between 2.5 to 4.5 kbps. Check Taidacent product page for more information.


The main controller is an Arduino mega 2560 it is on a separate mezzanine board than the HV parts / PZEM Modules.
The following IC : ADG333 allows routing a single hardware serial port to the three PZEM modules. Querying is continuous and sequential (all parameters of module 1,2,3,1,2…)
It could be technically feasible to have all PZEM modules on the same RX/TX serial TTL pair, but that would need to ensure that the optocouplers do not overload the RX/TX current sourcing capabilities, and also would need a factory preset of the PZEM-004t V3.0 addresses. Note that PZEM-004t V3.0 itself is a modbus device.
The server itself encapsulate telemetry queries and present the 3 PZEM-004t V3.0 as a single modbus server device to the Raspberry Pi client.
Thus, it seems more adequate to use the same Modbus address on each module, and switch them using an IC switch like the ADG333.

Time keeping & Synchronisation

A real time clock (RTC) 3231 mini module is used to keep track of time and the synchronization is done over PLC / Modbus.
Modbus address encoding of the server is done throught a 8 bit DIP switch encoder.

Software (check github repository)

Most of the single phase functionality is POC tested.three phase hardware testing and harderning is still in progress.Software is still in POC phase. Consider the code to be in alpha stage.

Hardware Schematics

Logic Board – Low Voltage
This is the logic mezzanine board.

Power board – High Voltage
HV PCB Layout

Software library includes & Credits

Modbus library used for PLC client/servers communication ArduinoModbus depends on the ArduinoRS485 library sandeepmistry

specific modbus conditioning and parsing library for the power monitoring modules mandulaj
library for timekeeping PaulStroffregen

library for scheduling task by timer ISRs khoih-prog
library for parsing math and logical expressions (for relay logic) zserge

The Synth1 project

For the MCU code, please visit :

For the Circuit / PCB design (work in progress) please visit :

A Digitally Controlled XR-2206 based VCO




The goal of this project is to implement a XR-2206 based VCO with digital control of frequency, amplitude, wave shape, and duty cycle, maximizing the use of on-chip capabilities of the XR-2206.

This project may be used as a standalone function generator. A MCU is used for tuning of the aforementioned parameters. Hence, This is why it is said to be « digitally controlled » compared to manual potentiometer control.

The incentive for using an analog XR-2206 VCO instead of a simpler DDS (Direct Digital Synthesis) IC is to offer a solution that exploits the unique feature of the XR-2206, as waveform symmetry adjustment and THD control.

In some situations, injecting an analog signal with its artifacts may be of interest.

As an analog oscillator, the XR-2206 is free from digital aliasing artifacts.

It is also a good choice for musical instrumentation as an analog synthesizer, as it’s unique features (symmetry adjust / THD control / wave-shaping from sine-wave to triangle wave) give warmth to the generated sound, which is sought after in the analog synth market.

It also allow a faster development than a full-on discrete analog oscillator design.



Presentation of the XR-2206 IC


The XR-2206 is a VCO containing two sub-oscillators, with a keying pin allowing the selection of either sub-oscillator for output on two signal output pins, one presenting a sine/triangle/saw shape, the other pin being the output of a square wave signal.

Frequency Control

Current controlled

Coarse frequency control is achieved through the use of a given resistor to ground from the frequency current control pin. Each oscillator has its dedicated frequency control pin.

Frequency range (common to both oscillators) is further achieved through the use of a capacitor on the capacitor timing pins.

The resistors and capacitor are further referred as « timing resistors » and « timing capacitor ».

The resistor pins are further referred as timing current pins.

This basic frequency control achieved in this manner is dependent on the current flowing through the resistor timing pins, hence, this setup gives, strictly speaking, ​​ a current controlled oscillator for frequency selection.

The data sheet states the minimum value for the resistors being 1K, in order not to exceed the safe margin for current sourced from the timing pins.

The formula for the frequency of oscillation for each oscillator being 1/(R_subosc*C), C being the capacitance of the frequency range capacitor common to both oscillators.

The timing current pins are internally biased at +3V

A coarse MCU control of frequency can be achieved in this manner through the use of digital potentiometer wired as a rheostat.

Renesas X9C10x series were demonstrated to work well in this setup, as the voltage to ground is inside the specification range, When using X9C102, a trimming rheostat must be included so the current sourced from the pin does not exceed the safe specification margins of both the digital potentiometer and of the XR-2206. (values??)

A more granular current control scheme can be achieved by using the digital potentiometers X9C104, X9C103, and X9C102 as rheostat in series to ground to achieve at 10 ohm step.

However, this approach has drawbacks :

X9C10x series have up to 20 % tolerance in total R_High to R_Low values, although the resistance value of each R step on the R ladder demonstrates good linearity.

Thus, It is imperative to carefully select matched digital potentiometers for both sub-oscillators and measure precisely the resistance of each digital potentiometer so that the MCU can tune the frequency with minimum readjustments.

Also, the use of multiple digital potentiometers in series makes the frequency tuning control algorithms more complex and each new potentiometer introduces error coming from the resistance measure and makes the time to tune longer. Careful design must be implemented so that out of tuning bounds events for each potentiometer do not happen, which would further delay the time to tune.

For all these reasons, a single digital potentiometer in rheostat mode for coarse tuning plus a DAC chip in voltage control mode for fine tuning is preferred as it reduces the number of digital control channels, while maintaining a large frequency range and good frequency resolution, while minimizing tuning time, but increasing BOM costs.

Nevertheless, if tuning time is not a priority, a lower cost hardware setup with higher control complexity algorithm can be devised.

Example of operation for an audio range frequency current control setup with three X9C10x digital potentiometer in series at each R timing pin.

In this setup, a polypropylene capacitor of 0.044 µF is inserted on the frequency range capacitor pins.

Provided that a trimming potentiometer ​​ in rheostat mode, is inserted in the series with one X9C104, one X9C103, and one X9C102 digital potentiometer in series, the minimum R_trim value setting the highest achievable frequency will be set so that the lumped resistance is no less than 1KΩ, stated by the XR-2206 datasheet as the minimum resistance for safe operation.

Additionally, the XR-2206 datasheet shows exponential degradation of frequency stability at large (>200 kΩ) and low (~1kΩ) timing resistors values. Thus, it is not advisable to use digital potentiometers with a total lumped resistance above 200 kΩ. This is the primary factor restricting the range of output frequencies.

Obtaining several output range frequencies can be done by selecting different capacitors of a bank in 1 of n fashion.

The X9C10x series was selected for its low cost, up to 7V voltage maximum rating and current rating in the low mA range. It also features recall of last setting at power-up, if needed, with a durability of 100000 cycles per bit of the recall logic.

However, it does not offer logic query of the current setting. That means that the MCU should maintain the potentiometer states in RAM and EEPROM if a recall of the setting at power-up is required. However, in-board MCU also have a cycle write limit for EEPROM, thus a flash memory storage would be preferable for frequent updates.

In the case of a musical instrument, the settings of these potentiometers affect frequency only, not timbre, and thus can be discarded at power-off.

Determination of frequency range

On the X9C10x datasheet, it is stated that the minimum resistance of the digital potentiometer is 40Ω, corresponding to the wiper resistance, noted RwiperR_wiperin the following formula defining the minimum value of the trimming resistance based on three digital potentiometers in rheostat mode used in series :

3Rwiper+Rtrim>1kΩ3*R_wiper + R_trim > 1k %OMEGA

solving for RtrimR_trim, supposing RwiperR_wiper​​ being close to the nominal 40 Ohms value.

Rtrim>880ΩR_trim > 880 %OMEGA

And, with C set at 6.6e-8 F (0.066 µF),

We can compute the maximum frequency we can obtain :

fmax=110006.6E8f_max = {1} over {1000 * 6.6E^-8}

fmax=15151Hzf_max = 15151 Hz

The lowest achievable frequency, provided that the potentiometers are each at nominal values of, respectively, 100 kΩ, 10 kΩ and 1 kΩ, and set at RmaxR_max, so that Rmaxtotal=100k+10k+1k+880+340R_maxtotal = 100k + 10k + 1k +880 + 3*40  :

Rmaxtotal=112kΩR_maxtotal = 112k %OMEGA

fmin=1112E36.6E8f_min = {1} over {112E^3 * 6.6E^-8}

fmin=135.8Hzf_min = 135.8 Hz


In the following implementation of a VCO for a musical application, as a synthesizer, RtrimR_trimwas offset at a higher value so that :

fmin=f(C3)=130.8Hzf_min = f( C3 ) = 130.8 HzThe three digital potentiometers being set at full-scale (99,99,99)

Based on the aforementioned relation, We can solve for RtrimR_trimand RmaxtotalR_maxtotal

So that,

Rmax=115,84kΩR_max = 115,84 k %OMEGA

Rtrim=4.717kΩR_trim = 4.717 k %OMEGA


Note that by doing this, fmaxf_max​​ is then offset to a lower value of 3132 Hz.

Which gives us a frequency range close to 4.5 octaves. (C3 to F#7)

Frequency tuning step resolution


Since the three digital potentiometers have 100 equal R steps (from 0/99*r_max to 99/99*r_max) and are fully overlapped, in the sense that no R setting is not accessible int the ​​ [Rmintotal,Rmaxtotal][R_mintotal , R_maxtotal], the RstepR_step​​ of the lowest resistance digital potentiometer (X9C102) gives the resistance step resolution, that is,

Rstep=1E399R_step = {1E^3} over {99}

Rstep=10.1ΩR_step = 10.1 %OMEGA

Which gives a frequency resolution at fminf_min​​ of :

Δfmin=1(115.84E310.1)6.6E81(115.84E3)6.6E8%DELTA f_min = {1} over {(115.84E^3 - 10.1)*6.6E^-8} - {1} over {(115.84E^3)*6.6E^-8}

Δfmin=0.0114Hz%DELTA f_min = 0.0114 Hz

and at fmaxf_max:

Δfmax=1(4.837E310.1)6.6E81(4.837E3)6.6E8%DELTA f_max = {1} over {(4.837E^3 - 10.1)*6.6E^-8} - {1} over {(4.837E^3)*6.6E^-8}

Δfmin=6.5544Hz%DELTA f_min = 6.5544 Hz

In terms of musical units, the interval compared to C3 at fminf_min​​ is :

Δfmincents=1200log2(f(C3)+Δfminf(C3))%DELTA f_mincents = 1200*log2({f(C3) + %DELTA f_min} over {f(C3)} )

Δfmincents=0.1508cents%DELTA f_mincents = 0.1508 cents

And at f_max = 3132 Hz

Δfmaxcents=1200log2(3132+Δfmax3132)%DELTA f_maxcents = 1200*log2({3132 + %DELTA f_max} over {3132})

Δfmaxcents=3.619cents%DELTA f_maxcents = 3.619 cents

Dynamic Tuning Algorithm

Single Sub-oscillator tuning

A fully digital algorithm based on the Arduino Mega 2560 was devised.

Measuring the frequency was achieved through the improved resolution timer2 library and a modified version of the PulseIn that integrates the 0.5µs precise timer2 library instead of the 4µs standard precision.

It should be noted that accurate frequency measurements are dependent on the clock frequency of the Arduino. Arduino mega 2560 is clocked at 16 Mhz, However it uses a ceramic oscillator instead of Quartz crystal oscillator. Thus, it is probable that the oscillation is not an exact 16 Mhz.

This is not a problem for interval tuning since it is based on frequency ratios, but may introduce de-tuning if the synthesizer signal is to be integrated into an ensemble of other instruments using, for instance, a reference tuning of A4 = 440 Hz.

Also, The higher the working clock frequency of the MCU, the higher is the measure resolution. and tuning speed. However, the limiting factor in the tuning speed achieved is the period of the signal. Measuring the frequency is usually done through signal level or edge triggering or zero cross  ​​ ​​​​ method, all requiring at least a lapse of time of 1/f_fundamental to compute the frequency of the signal.

In practice, oscillator drift, noise, may require averaging several frequency measurements to obtain a reliable measure.

In this implementation, the number of frequency measure sample has been set to 10.

An analysis of the statistical distribution of frequency measures over a medium interval (like 10s) will give an idea of the effects of noise, digital aliasing artifacts. Oscillator drift is usually temperature dependent and should be minimized by using an adequate capacitor with a flat temperature to capacitance response. (temperature drift compensating)

In this implementation, low ESR metalized polypropylene capacitors are used, for their adequate frequency response, long lifetime and parameter stability over time.


The first step in tuning uses the f = 1/RC formula, solved for R. It is important to have a precise measure of the timing capacitor capacitance at standard temperature conditions of use.

R = 1/fC.

Now, three values of resistance should be determined satisfying the following conditions :

R1 + R2 + R3 = R – Rtrim - Rwipers

R1 < 100K

R2 < 10K

R3 < 1K

Ideally, R2 and R3 values should be as close as possible to the mid-range of the digital potentiometer, that is, 10K/2 and 1K/2. This gives leeway for fine tuning of R2 and R3 in both increasing and decreasing R steps.

Based on the above formula, the three digital potentiometers are set to their respective values, and the sub-oscillator frequency is measured.

The frequency error is then : ferror=ftunedfmeasuredf_error = f_tuned – f_measured

The goal being to tune the oscillator at the R value of min(|ferror|)min(abs{f_error})

A standard gradient descent approach based on the derivative of frequency over resistance function is used dRdf=1f2C{dR} over {df} = {-1} over {f^2 *C} so that the step delta_R for each R correction is given by :



ΔR1f2Cferror%DELTA R simeq {-1} over {f^2 *C} * f_error

In practice, a tuning factor k is introduced and determined empirically in case of undershoots /overshoots, k < 1 in case of overshoots and k > 1 in case of undershoots

ΔR1f2Cferrork%DELTA R simeq {-1} over {f^2 *C} * f_error * k

Considering a simple algorithm that would make corrections on the digital potentiometer R values sequentially, starting with the highest value potentiometer for coarse tuning. The potentiometer step change number is given by the rounding to the nearest integer function [][<?>] ​​ :

ΔRsteps=[ΔRRpotstep]%DELTA R_steps = [ {%DELTA R} over {R_potstep} ]

​​ being the value in ohms of the digital potentiometer under consideration

For each potentiometer, a ΔRsteps%DELTA R_stepstuning deflection is applied and ferrorf_erroris sampled. Depending on the tuning factor k, the following conditions will arise.

For a sub-optimal too low k, the series made of each step ferrorf_error​​ will converge to 0 without sign change, the less optimal the k, the more steps before reaching the condition :

ΔR<Rpotstep{%DELTA R} < {R_potstep}


For a sub-optimal too high k, the series made of each step ferrorf_error​​ will converge to 0 with zero crossings at each tuning step , the less optimal the k, the more steps before reaching the condition :

ΔR<Rpotstep{%DELTA R} < {R_potstep}

It may be useful to have a slightly sub-optimal too high k, so that a zero-crossing for ferrorf_errorcondition is achieved faster after a last tuning step of exactly1Rpotstep1 R_potstep

So that min(|f(error(n1))|,|f(error(n))|)min(abs{f_(error(n-1))}, abs{f_(error(n))})decides if the last tuning step is discarded or conserved.

However, The smallest potentiometers current settings should be taken into account before selecting the higher value potentiometer setting such that an out of tuning bound condition may not arise.

The process is repeated for the smaller potentiometers ​​ until the ferror<ferrorthresholdf_error < f_errorthresholdcondition arises.

In this implementation, ferrorthresholdf_errorthreshold​​ was set at ± 2 musical cents.

The tuning algorithm speed is a function of the number of tuning steps times the tuning time per step. The tuning time per step if made from computational overhead and most significatively, the time it takes to measure the signal frequency times the number of frequency measure samples.

ttuning=nsteps(nsamples(tperiod+tcompmeas)+tcomptuning)t_tuning = n_steps * (n_samples * (t_period + t_compmeas) + t_comptuning)

n_steps can be optimized as already discussed above by fine tuning k and by a precise measure of C  ​​​​ and RmaxR_maxand by selecting a proper capacitor (metalized polypropylene film is a good choice and metal film resistors with low inductance parasitics, although these considerations are of prime importance in higher frequency ranges only, less in audio, except for the capacitor regarding temperature dependency), so that the circuit deviates as less as possible from 1/RC law.

Algorithmically, n_steps can also be reducing by stepping all required digital potentiometers at each  ​​ ​​​​ 

step instead of stepping them sequentially, as it will be described in the optimized algorithm in the next section.

nsamplesn_samples can be optimized by analyzing the statistical distribution of frequency measures, as it will impact the precision of the obtained frequency, taking into account a goal of 2 cents in our application, note that the smallest time component that can be measured for a single suboscillator is :

thalf=12ft_half = {1} over {2*f}as it is the time interval returned by either PulseIn(LOW) or PulseIn(HIGH) for a 0.5 duty cycle signal. Thus the tuning time will be strongly dependent and inversely proportional to the frequency. Reducing noise by using proper circuit design and adequate low pass filtering on the measuring frequency pin is of prime importance.

The worst case tuning speed obtained was close to 1.5 seconds (fr C#3) and close to 0.4 seconds at high frequencies (B#6). Using the naive sequential digital potentiometer tuning method described above.

tcompmeast_compmeas​​ is very light in computing time, and generally only decreases the frequency measure resolution, because that overhead is compounded into the frequency measure value. Using a faster Arduino MCU (like The Due) with a precise XTAL Quartz oscillator and a  ​​​​ precise Timer library will improve measuring precision and resolution substantially.

tcomptuningt_comptuning can be minimized by applying general Arduino coding best practices, using extra care on the computational intensive parts of the algorithm.

For all these reasons, a debugging mode with timing display should be used to quantify performance gains and overall tuning duration performance.

Optimized parallel tuning algorithms

If the deviation from theory is small, the overall delta R may be sufficiently small from the theoretical value that only the lowest R digital potentiometer is stepped to obtain accurate frequency

In other cases, if the deviation requires a tuning step above the highest digital potentiometer R step,

A parallel tuning may be experimented with and compared to the sequential tuning in term of performance. This may work well if each potentiometer has good step linearity and each potentiometer R value is measured precisely.

Note that for the theoretical determination of each potentiometer step, it is good practice, if the R value make it possible, to bias the medium and low value digital potentiometer at half scale, so it gives leeway for up and down stepping. (~5k for X9C103 and ~0.5k for X9C102)

Then, the delta R, once computed, should be distributed across the potentiometers. Since the potentiometers are overlapping, there is more than one way to distribute delta R across them.

The selected way should avoid if possible to step potentiometers close to their stepping bounds. (0 , 99)

Consider the following example to illustrate the algorithm :

Tuning for f(A4) = 440 Hz,

We have, R=14406.6E-8R = {1} over {440*6.6E-8}

R = ​​ 34 435 Ω

Let’s first take R_trim out including wiper resistances :

R_pot = 34435 – 4.8237

R_pot =29 608

Now let’s bias the medium and low potentiometers to ~5K and ~0.5K (Supposing that they were chosen to have nominal values).

R_pot_remaining = 29608 – (49/99)*10000 – ​​ (49/99)*1000 = ​​ 24163 Ω

Let’s perform the division of that value by the highest pot R step and half-step round to the closest integer.

24163 / (100 000/99) = 23.92137

In that case, we will select the R step at 24.

That means that the reminder will be negative, and thus subtracted from the biased medium/low pots.

The reminder being 24163 – 24*(100 000/99) = -79.42 Ohms.

Now we have to distribute the reminder across the X9C103 and X9C102 pots.

Let’s repeat the division scheme

-79.42 / (10 000/99) = - 0.7862

In that case we select a step of -1.

The reminder being -79.42 - (-1*(10 000/99)) = 21.59 The reminder is once again positive, since we overcompensated.

Let’s do it one last time/

21.59 / (1000/99) = ​​ 2.13741

21.59 \ (1000/99) = ​​ 2 and the remainder is 1.38

​​ Now let’s add these values to the potentiometer initial biasing.

XC9103 steps = 49 - 1 = 48

X9C102 steps = 49 + 2 = 51

Let’s recheck the R obtained :

24*(100 000/99) + 48 *(10 000/99) + 51 * (1000/99) = ​​ 29606.06 (~29608 being the R value above that we approximated)


Compound oscillator with arbitrary pulse width tuning

For a given fundamental frequency and duty_cycle, the two sub-oscillator requested frequencies are determined from theory and their corresponding potentiometer settings.

Since The Arduino PulseIn can discriminate between LOW levels and HIGH levels, the frequency of ​​ individual sub-oscillator can be measured from the compound signal, in self-keying mode, without the need to toggle the FSK pin.

f_subosc = 1/(2*t_level)

We did not observe 180° phase reversals of the compound signal in our setup, which would also switch duty cycle between duty and 1 – duty, Which is good since it makes identification of the tuned sub-oscillator easier.

The tuning method used above would tune the sub-oscillator with the highest frequency first. Using PulseIn (LOW / HIGH) the sub-oscillator frequency is extracted and tuned.

Then, the sub-oscillator with the lowest frequency component would then be fine tuned taking into account the fundamental frequency of the compound signal.

Using the frequency component of the remaining sub-oscillator instead of the compound signal fundamental frequency for the last step of tuning yields substantial error in our setup. Thus it is avoided and instead the fundamental frequency is used in the last sub-oscillator tuning step.

The detrimental effect this method has is that it introduces error in the duty cycle setting. However it is less critical than an error in the fundamental frequency.

Static Tuning Algorithm

If the circuit under consideration exhibits deviations from theory or a precise measure of the components could not be achieved, and the tuning time is deemed unacceptably high, as it may be the case for a musical instrument where the fundamental frequency must be set at once, it is possible to use the above tuning algorithm and store the potentiometer values determined for a set of discrete frequencies in EEPROM of flash.

While the number of entries in the lookup table is reduced for a single sub-oscillator (usually 12 notes per octave), The number of entries is greatly increased in the case of a compound oscillator based on the two sub-oscillator (for pulse widths different from 0.5).

The number of entries will then directly influence the fundamental frequency resolution ​​ and/or the duty cycle resolution.

Lookup Table memory footprint

Each digital potentiometer has a 100 step resolution. A byte memory variable can then store 2 digital potentiometer settings. Each sub-oscillator uses 3 digital potentiometers. For a given frequency, the settings of the two sub-oscilllators can be stored using 3 bytes.

A two cents separation lookup table for frequencies from C3 to C7 would then store 48*50 = 2400 intervals.

Such an optimized lookup table would use 7200 bytes of memory. These values would not need to be changed at run-time so burning them in the Arduino Mega flash (256KB) is adequate.

Compound oscillator tuning based on lookup tables.

As for the dynamic tuning, The two sub-oscillator frequencies are computed from a given waveform with frequency and duty cycle parameters.

The closest frequency settings of the digital potentiometers are then extracted from the lookup tables.

By using interpolation of two adjacent potentiometer settings, further precision of the tuning can be achieved.

The downside of the static tuning method is the length of time it takes to create the initial tuning map, (or whenever a timing digital potentiometer, capacitor, or XR-2206 chip is replaced) and eventual deviation in case of poor component stability with time.

Thus, it is preferred to have both methods at hand and use one or the other, or a combination of both depending on the situation.

It should be noted too that the X9C10x digital potentiometers are resilient but simple devices. They are increment/decrement step based devices with last set value recalled at power-up. The current setting however, cannot be queried. It is then compulsory to store the current setting in RAM to track the current value of the potentiometer by integrating all step changes during the power cycle and taking care not to step out of the setting bounds.

A multiple hour operation however has not shown any discrepancies between the in RAM setting and the potentiometer R value. Noise or power supply issues could cause the potentiometer to skip a commanded change, but it was not observed in our breadboard prototype.

For good measure, an out of bounds increase/decrease command can be sent with an increase or decrease value greater than the number of steps to reset the potentiometer to its lower or upper bound.

In the case of a musical synthesizer, this should be done when the VCA is at rest, so no artifact sound is played.













Voltage Controlled operation

The XR-2206 can be controlled by a voltage input ranging from 0 to 3V, in the following manner, as  ​​ ​​​​ per the datasheet :

The voltage to frequency formula is then :

f=1RC(1+RRC(1VC3))f = {1} over {RC} ( 1 + {R} over { R_{C}} (1 - {V_{C}} over {3} ) )

In this setup, the sweep input is connected to the output pin of a voltage DAC. The output stage of the DAC must be able to sink current.

The voltage range of the DAC will be restricted to [0, 3V]

For safe and reliable operation, We have to size RCR_C​​ and R so that It3mA I_{t} leslant 3mA

If VCV_C​​ is set at 0V, ItI_twill be :

3RRCR+RC<3mA3 * {R*R_C} over {R + R_C} < 3mA

Supposing for design simplicity that RCR_Cis a fixed lumped resistance and that R is variable and that ​​ RCRminR_C >= R_min

It follows that R and RCR_C​​ should be sized to at least 2 kΩ

Note that the DAC output impedance stage should be taken into account for RCR_C

Considering RCR_Cand C fixed, the frequency setting is obtained through R and VCV_C

We have now to degrees of freedom to set the frequency.

We will first ensure that the obtain frequency range in this setup goes at least from C3 to C7

The principle of operation in this setup uses VCV_C​​ voltage in the [0, 3V] range, for fine tuning of the frequency, with a resolution dependent on the bit resolution of the DAC

R, as a digital potentiometer in rheostat mode, for coarse fine tuning. A too small R digital potentiometer would severely restrict the frequency range. So, we should consider first the highest R value digital potentiometer, the X9C104 of RmaxR_max​​ = 100k Ω

Since in this setup we are using a single X9C104, And since we will also make use of a trimming resistor RtrimR_trimso that At RmaxR_max​​ f=f(C3)f = f( C3 ), by increasing RtrimR_trim​​ to compensate for the removal of the X9C103 and the X9C102, thus adding 11 kΩ , we may not reach C7 at RminR_min

Indeed, with RtrimR_trim​​ = 15.827 kΩ, at Rmin=RtrimR_min = R_trim, ​​ f=115.827E36.6E8=967.32Hzf = {1} over {15.827E^3*6.6E^-8} = 967.32 Hz

That is well under our target of at least 2093 Hz.

However, if we carefully select a X9C104 in high portion of the tolerance range of the manufacturer specifications ( ± 20%), This would decrease the required R_trim value.

In our component survey of over thirty X9C104 chips we found five with a RmaxR_maxvalue of over 110kΩ

So, For a total RmaxtotalR_maxtotal​​ of 114.827 kΩ and a X9C104 RmaxR_max​​ of 110 kΩ, RtrimR_trim​​ = 4.827 kΩ, which gives a maximum frequency with VCV_C= 3V, of :

f=14.827E36.6E8=3138.9Hzf = {1} over {4.827E^3*6.6E^-8} = 3138.9 Hzwell above C7, which is good.


We should then check that frequency control usingVCV_C and R, in the above defined frequency range is fully overlapping and thus encompasses fine tuning over the range C3 to C7.

Provided that the output stage of the DAC is scaled down through a less than unity non inverting OpAmp so that the n bits resolution is applied to the range [0 , 3V]

Assuming a 12bit DAC with it’s output range restricted to [0, 3V] through a voltage divider or better, an OpAmp with less than unity gain to keep output impedance low :

dVC=3212=0.732mVdV_C = {3} over {2^{12}} = 0.732 mV

Given the voltage to conversion gain formula, δfδVc=0.32RcC{ %delta f } over {%delta V_c} = {-0.32} over {R_c * C} Hz/V

We have to set Rc so that each voltage step has a frequency gain under 2 cents at C3, according to the following relation :

ferrorcents=1200log2(f(C3)+fstepf(C3))<2centsf_errorcents = 1200*log2({f(C3) + f_step} over {f(C3)} ) < 2 cents

Solving for step, step < 0.16021 Hz

With unitary voltage step of 3/2^12 = 0.732 mV

And the voltage to frequency gain, δfδVc=0.32RC6.6E-8{ %delta f } over {%delta V_c} = {-0.32} over {R_C * 6.6E-8} Hz/V

δfδVc=0.32RC6.6E-80.732E-3=0.16021Hz{ %delta f } over {%delta V_c} = {-0.32} over {R_C * 6.6E-8} * 0.732E-3 = 0.16021 Hz

​​ for a step of 0.732 mV, that gives a minimum Rc of 22152 Ohms

And a maximum frequency deflection over the 3V range of

δfδVc=0.32221526.6E-83=656Hz{ %delta f } over {%delta V_c} = {-0.32} over {22152 * 6.6E-8} * 3 = -656 Hz


At VCV_C​​ = 3V, The formula reverts to the form 1RC{1} over {RC} ​​ with its derivative being

δfδR=1R26.6E-8Hz/ohm{ %delta f } over {%delta R} = {-1} over {R^2 *6.6E-8} Hz/ohmat R so that f = f(B6), R= 7671 ohm, We have a slope of -0.2574 Hz/ohm, for a step of 1.1Kohm (based on a X9C104 select with RmaxR_max= 110K), we have 283.14 Hz/RstepHz/R_step

This is under the 656 Hz total voltage deflection, So frequency control based on VCV_C​​ in the [0,3V] and RstepR_step​​ = 1.1kOhm is overlapping at the high end of the frequency range.

The upper bound for RCR_Cis given by the relation

δfδVc=0.32Rc6.6E-83>283.14Hz{ %delta f } over {%delta V_c} = {-0.32} over {Rc * 6.6E-8} * 3 > -283.14 Hz

Which give a maximum value for RCR_C​​ of 51371 Ohms.

Thus the allowable range for RCR_Cis :

22152 Ohms < RCR_C​​ < 51371 Ohms.

In practice, fine tuning precision is more important than fine tuning range once overlapping tuning is guaranteed. Thus it is preferable to bias RCR_C​​ toward the upper part of the [22152, 51371 Ohms] range.

However, taking advantage of higher precision fine tuning requires to invest more on the MCU (higher clock speed and calibration).

Reminder : Worst case scenario precision is 4µS on a AtMEGA2560

On the other hand, tuning ranges with larger depths may have their use for pronounced glide/vibrato effects. And having a large tuning range gives a higher probability of tuning at ± half semi-tone of the desired frequency based on voltage control alone. That depends however for each note at how close the fundamental is tuned to half voltage deflection (VCV_C = 1.5V).

If the ± half semi-tone glide exceeds the DAC range because of initial deviation from the 1.5V VCV_C​​ center position, A step up/down of the digital potentiometer is required, which would induce an audible discontinuity in the vibrato / glide effect.

This is one fundamental issue of having a tuning method based on two discrete components (a digital potentiometer and a DAC), but is less pronounced that having the tuning relying on three digital potentiometers alone.

Tuning algorithm based on DAC + single X9C104 digital potentiometer.

This algorithm is intended to be simple and fast.

First, coarse tuning should be done at VCV_C​​ = 3V so that the frequency formula reverts to the classic 1RC{1} over {RC} form.

A lookup table consisting of the obtained frequencies of the 100 steps of the X9C104 potentiometer could be populated if it happens that there is a deviation in obtained frequencies from the 1RC{1} over {RC} ​​ form. All things considered, the small footprint of the lookup table make it easy and fast to populate, so even if the gain in MCU computing time is marginal, it costs little to use one.

Since the voltage to frequency gain is negative, lowering the DAC voltage from its 3V rest point results in an increase in frequency.

Then, for a given arbitrary frequency, the digital potentiometer should be set at the setting level that result in a frequency immediately under the desired frequency.

And then, the voltage lowered according to the voltage to frequency gain formula to obtain the positive ferrorf_error​​ deflection.


Compound oscillator with variable pulse width tuning

The algorithm is similar to the string of digital potentiometers in series algorithm , in the sense that the sub-oscillator with the highest frequency component is tuned first using PulseIn (LOW or HIGH)

using the digital potentiometer for coarse tuning and the VCO voltage gain formula for the fine tune step.

The remaining oscillator is then tuned to the frequency immediately under the desired frequency.

Using the voltage to frequency gain formula, When measuring the error on the resulting signal ferrorf_error, the frequency error on the component sub-oscillator signal fcmperrorf_cmperror​​ is adjusted to

fcmperror=ferror2duty,whenduty>0.5f_cmperror = {f_error} over {2*duty} , when duty >0.5


fcmperror=ferror2(1duty),whenduty<0.5f_cmperror = {f_error} over {2*(1 -duty)} , when duty <0.5

Since we are tuning the lowest frequency component last.

Appendix A :

Duty Cycle control (SAW signal generation and SQUARE pulse width control)

Duty cycle / pulse width control is achieved through self-keying between the two sub-oscillators by routing the square wave pin output (SYNC0) to the keying (FSK) pin.

The FSK pin is level triggered (1V 3V) and selects sub-oscillator 1 or sub-oscillator 2. When self-keyed from SYNC0, The output is adjusted for phase in the following manner.

SYNC0 level is high when the TRI/SAW signal rises.

SYNC0 level is low when the TRI/SAW signal falls.

When FSK pin is triggered high, the output signal switches to sub-oscillator 2, with phase at Vmin, and the TRI/SAW signal rises, SYNC0 level stays high. The sub-oscillator 2 rising edge duration is

trise=12fsubosc2t_rise = {1} over {2*f_subosc2}

When sub-oscillator 2 reverts to the falling edge, SYNC0 level goes down and sub-oscillator 1 is selected, with phase positioned at VmaxV_max. SYNC0 level stays low. The sub-oscillator 1 falling edge duration is :

tfall=12fsubosc1t_fall = {1} over {2*f_subosc1}

When self keyed, the signal fundamental frequency at output pins is :

ffundamental=1trise+tfallf_fundamental = {1} over {t_rise + t_fall}

and the duty cycle is :

duty=trise(trise+tfall)duty = {t_rise} over {(t_rise + t_fall)}