Spin Coater

To start using Persistent Micelle Templating, you are going to need to be able to coat substrates under controlled humidity conditions. Commercial spin coaters with temperature and humidity control cost a lot of $$$.  On the other hand, the cheapest approach of attaching your sample to a computer fan with double-stick tape leaves much to be desired. Our home built setup includes most of the best features from commercial spin coaters (vacuum chuck, set speed & time, speed ramps, humidity & temperature) at a fraction of the cost.

Meet our spin coater:


The core components are:

  1. Arduino Uno controller with push button LCD screen and breakout board

  2. A rotor motor from a quadcopter. Get one with a hollow shaft so it is easier to make the vacuum chuck

  3. A bubbler setup with a hot plate to produce air with controlled humidity and temperature

  4. Tupperware enclosure for the spin coating environment. We like the Gladware variety.

Other bits include:

  1. Electronic speed controller

  2. RPM sensor

  3. Enclosure

  4. Spare computer power supply

  5. Solenoid valve, 2 hose adapters

  6. Corrosion-X, a corrosion-inhibiting lubricant

  7. Heat shrink tubing, electrical tape, resistors, switches, soldering iron

Do-It-Yourself Instructions:

  1. Power. Most of the spin coater components will need electricity.  Here, an old computer power supply is perfectly suitable for the 5 V needed for the Arduino and LCD screen as well as the 12 V needed for the motor and the solenoid valve.  You can clip the green wire to any black wire to turn the power supply on and off. This will be need to be cycled any time the power supply turns off due to excessive current. You can figure out which wires are needed for each voltage by googling “ATX pinout diagram.”


  2. Vacuum. The solenoid valve controls the application of vacuum to the vacuum chuck. It is pictured just to the left of the power supply in the above photo. The setup here is straightforward by tapping into the 12 V power lines with a toggle switch. A tube connects from the solenoid valve outside of the enclosure to your house vacuum. A separate tube connects the other end of the solenoid valve to the back of the rotor. Our machine shop made a nice little mounting plate to direct the vacuum to the back side of the rotor. We have used hot glue in the past with similar success.

    vac mount

  3. Motor. Our machine shop made a small teflon drip guard to keep liquids out of the motor. A nut with o-ring holds the splash guard to the bellhousing of the motor. The motor shaft of these DYS motors is mostly hollow and almost a thru hole – we needed to drill out a few mm with a carbide bit so that the vacuum on the backside of the motor mount reaches up to the vacuum chuck and o-ring. This allows for clean and easy sample mounting without double stick tape.IMG_9458

  4. Arduino LCD Interface. Arduino makes this build relatively easy with cheap, off-the-shelf components. Here a ~$15 Arduino shield with LCD and push buttons handles user input and displays the status. The software runs on a ~$10 Arduino Uno. The code is provided below.


  5. Speed Control. The rotor motor is a simple 3 phase design where voltage is applied to alternating pairs to move the motor step-wise. Getting the timing just right is tricky, so fortunately all the hard work was done with an off-the-shelf electronic speed controller (ESC). Here, the non-active lead is used as a sense to know when to give the next push to keep the motor running – that allows it to maintain proper timing and also to respond to changing loads dynamically.

    IMG_9456The ESC has 3 output wires that connect directly to your rotor motor. The ESC also has a red and a black wire for voltage to drive the motor. Here if you use a 12 V line from the power supply it is approximately equivalent to a 3S battery. Thus this 2S-4S rated ESC is fine for our setup. The twisted pair of red/white wires is the control signal from the Arduino. A pulse-width modulation (PWM) is used to indicate speed, more on that later.

  6. Sample Chamber. Tupperware is just fine at providing a local environment for your samples. Just add an inlet hole for your environment control gas, put on the lid, and you are good to go. Some bolts will keep it in place, just in case a sample comes loose while spinning.


  7. Humidity Control. A dual-flow controller setup provides for easy tuning of humidity. One line passes dry compressed air directly towards the sample chamber. The other line passes through a submerged aquarium stone to produce nearly-saturated vapor. The gas emerging from the bubbler is then mixed with the dry air and passed through a thermostated copper coil to maintain constant temperature and humidity. The mixing ratio of dry to wet air determines the humidity. Please note that some purge time is needed in between samples for the chamber to equilibrate to the desired humidity. The needed time is easily estimated with the Basic Room Purge Equation – here 1-2 min with 25 LPM of flow is plenty sufficient for a small tupperware.


  8. Speed Calibration. The ESC will likely need some adjustment before use. First, update the ESC to the latest firmware with the BLHeli Suite. In the settings, be sure to set the ESC to “closed Loop Mode,” that way it translates the input PWM signal to specific RPM targets and regulates the control with a PID loop. Use the Motors tab to then ramp your motor up and down in throttle. You can use a second Arduino board to monitor the rotor RPMs with an RPM sensor and some simple code, provided below. Be sure to use a 1k ohm resistor on the output of the RPM sensor. Also note that the wire colors on the RPM sensor are counterintuitive – we normally change the code to the standard black for ground and red for 5V input. Adjust the “closed loop P-gain” and “closed loop I-gain” to get a smooth RPM response without oscillations (intro to PID tuning). Depending on your motor, you may also need to adjust the “Motor gain” and the “startup power.” (Intro to everything BLHeli)BLHeliSuiteSiLabs ESC Setup_170120_3

    We had trouble getting to low RPMs with the DYS motors specified above, so we added 2x1ohm resistors for each of the 3 connections from the ESC to the motor. With that adjustment we can now spin coat from 300-7000 RPM. Below you can see the “brick” of resistors we soldered together to enable low-RPMs.

    IMG_9435Once that is done, you will need to calibrate the ESC. This type of ESC has a lousy internal clock, so you the first thing needed is to calibrate the PWM signal relative to the Arduino output. Set the Arduino to output a 2000 us signal, apply power to the ESC and you will hear some musical chimes indicating the high-signal was recorded, quickly change the output to 1000 us and another musical chime will sound indicating the the low-signal was recorded (calibration tips). Power cycle the ESC to start it with the new settings. Next, use the first Arduino to send a series of PWM signals, ranging from 1020-2000us and record the resulting RPM values with the second Arduino (helpful notes). A simple graph should show a linear correlation of signal to RPM if the PID parameters are properly set.

    calibrationUse the fit equation to calculate your the PWM signal needed for your target RPM values.

  9. Humidity Calibration. A calibration chart should be made for the ratio of wet:dry air and the corresponding relative humidities. A linear trend is expected, however note that many hygrometers are not able to resolve <20%RH. Thus extrapolation is an option for low humidity targets after calibration at higher %RH.

Arduino code for RPM measurement:

#include <FreqMeasure.h>
void setup() {
double sum = 0;
int count = 0;
void loop() {
 if (FreqMeasure.available()) {
    // average several reading together
    sum = sum + FreqMeasure.read();
    count = count + 1;
     // Serial.println(“measuring”);
    if (count > 10) {
      float frequency = FreqMeasure.countToFrequency(sum / count);
      float rpm = frequency *2 /14 *60;
      sum = 0;
      count = 0;

Arduino code for Spin Coater with LCD screen:

#include <LiquidCrystal.h>
#include <Servo.h>
Servo myesc;

#define MAX_SIGNAL 2000
#define MIN_SIGNAL 500
#define ESC_PIN 13

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

//Key message
char msgs[5][15] = {“Right Key OK “,
“Up Key OK “,
“Down Key OK “,
“Left Key OK “,
“Select Key OK”
int adc_key_val[5] = {75, 250, 420, 620, 780 };
int adc_key_in;
int key = -1;
int oldkey = -1;
int speeds[] = {400, 500, 750, 1000, 1100, 1500, 2000, 3000, 4000, 5000, 7000};
int escvalues[] = {1093.9, 1106.1, 1136.7, 1167.2, 1179.4, 1228.2, 1289.3, 1411.4, 1533.5, 1655.6, 1899.8};
int times[] = {1, 1.5, 2, 5, 10, 20, 30, 45, 60, 120, 300, 600};
int speeds_size = sizeof(speeds) / sizeof(int) – 1;
int times_size = sizeof(times) / sizeof(int) – 1;
int selected_speed = 0;
int selected_time = 0;
String lyrics = “You spin me right round, wafer; Right round like a record, wafer; Right round round round; You spin me right round, wafer; Right round like a record, wafer; Right round round round “;
int lyrics_size = sizeof(lyrics) / sizeof(char) – 1;
void setup() {

//myesc.writeMicroseconds(MAX_SIGNAL); //option to calibrate ESC

myesc.writeMicroseconds(MIN_SIGNAL);//start with motor off

lcd.begin(16, 2);// set up the LCD’s number of columns and rows:
lcd.setCursor(0, 0); lcd.print(“Stefik Group 1.0”);// Print a message to the LCD.
lcd.setCursor(0, 1); lcd.print(“you spin me”); delay(800);
lcd.setCursor(0, 1); lcd.print(“round like a “); delay(800);
lcd.setCursor(0, 1); lcd.print(“record now “);
delay(800); //hold the load screen
lcd.setCursor(0, 1); update_display();
void loop() {
//lcd.setCursor(0, 1);// set the cursor to column 0, line 1 (note: line 1 is the second row, since counting begins with 0):
adc_key_in = analogRead(0); // read the value from the sensor
key = get_key(adc_key_in); // convert into key press

if (key != oldkey) // if keypress is detected

delay(50); // wait for debounce time
adc_key_in = analogRead(0); // read the value from the sensor
key = get_key(adc_key_in); // convert into key press

if (key != oldkey) //a new button press is detected!
oldkey = key; //avoid phantom presses durring continuous hold

if (key == 0) //right key pressed
if (selected_time != times_size)

if (key == 1) //up key pressed
if (selected_speed != speeds_size)

if (key == 2) //down key pressed
if (selected_speed > 0)

if (key == 3) //left key pressed
if (selected_time > 0)

if (key == 4) //select key pressed
//take 1 second to spin up
int mystep;
int numberofsteps = 10;
int nextspeed;
for (mystep = 0; mystep < numberofsteps; mystep++) //take 100 incraments from slowest speed to requested speed
nextspeed = escvalues[0] + (escvalues[selected_speed] – escvalues[0]) * mystep / numberofsteps;
myesc.writeMicroseconds(nextspeed);//set the motor speed
delay(1000 / numberofsteps);

Serial.println(“requested speed”);
int t; int cursorpos;
for (t = times[selected_time] – 1; t >= 0; t–) //continue spinning for [requested time minus spinup time]
cursorpos = (times[selected_time] – t) * 2;
lcd.setCursor(0, 0); lcd.print(lyrics.substring(cursorpos, cursorpos + 16));
lcd.setCursor(0, 0); lcd.print(lyrics.substring(cursorpos, cursorpos + 16));
lcd.setCursor(0, 1); lcd.print(“woosh! “);
lcd.setCursor(9, 1); lcd.print(t); lcd.print(“s”);
myesc.writeMicroseconds(MIN_SIGNAL);//turn off motor

} //
} //new key pressed
} //overall loop

void update_display() {
lcd.setCursor(0, 0); lcd.print(“Stefik Group 1.0”);// Print a message to the LCD.
lcd.setCursor(0, 1);
lcd.print(” RPM, “);
lcd.print(“s ” );

int get_key(unsigned int input) // Convert ADC value to key number
{ int k;
for (k = 0; k < 5; k++)
if (input < adc_key_val[k])
return k;
if (k >= 5)
k = -1; // No valid key pressed
return k;


This project was inspired by earlier works with spin coaters based on hard drive motors (article, video). That is actually what we built first, however our acidic solutions quickly ruined the aluminum hard drive motor. We have good reliability using rotor motors protected by Corrosion X. If a motor were to expire, they are easily replaced with identical parts available online with short notice.