Dosing pump on an Arduino Uno
A peristaltic pump on an encoder gear motor and an L9110S: press the button and it pumps exactly 5 ml, counting the encoder's pulses and slowing for the last turn. The whole build, an Arduino Uno and 4 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
IntermediateRuns in your browser. Free, and no account needed.
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Dosing pump: a peristaltic pump head on a gear motor with a Hall encoder,
// driven by an L9110S, that pumps a measured dose at the press of a button.
// The aquarium fertilizer doser, the plant feeder and the cocktail machine
// are all this.
//
// pin 5 the L9110S's A-IA (speed, PWM) pin 6 its A-IB
// pin 2 the encoder's A (an interrupt) pin 3 its B
// pin 4 the button to GND (INPUT_PULLUP)
// the pack's + to the L9110S's VCC; the encoder's VCC to 5 V; grounds joined
//
// A peristaltic pump moves the same volume every turn of its rotor, so the
// dose is a number of turns, and the encoder counts them. It gives 11 pulses
// a motor turn through a 30:1 gearbox: 330 pulses on A for each turn of the
// pump. This head moves 0.5 ml a turn (measure yours: pump 20 turns into a
// measuring cylinder and divide), so 5 ml is 10 turns, 3300 pulses.
//
// # Stopping on the mark
//
// A motor does not stop the instant it is told. So the pump runs at full
// speed until it is one turn short, slows to a crawl for the last turn, and
// brakes (both inputs high) a little early, at 45 pulses to go, for the few
// it coasts through. The count goes on after the brake, so the dose printed
// is what actually went through.
const int IA = 5;
const int IB = 6;
const int ENC_A = 2;
const int ENC_B = 3;
const int BUTTON = 4;
const long PULSES_PER_TURN = 330;
// Hundredths of a ml per turn of the pump head.
const long CENTI_ML_PER_TURN = 50;
const long DOSE_CENTI_ML = 500;
const long DOSE_PULSES = DOSE_CENTI_ML * PULSES_PER_TURN / CENTI_ML_PER_TURN;
const long SLOW_AT = DOSE_PULSES - PULSES_PER_TURN;
const long BRAKE_AT = DOSE_PULSES - 45;
const int FULL = 255;
const int CRAWL = 90;
volatile long pulses = 0;
void onA() {
// B low on A's rising edge is forward.
if (digitalRead(ENC_B) == LOW) {
pulses++;
} else {
pulses--;
}
}
long count() {
noInterrupts();
long n = pulses;
interrupts();
return n;
}
void run(int speed) {
analogWrite(IA, speed);
digitalWrite(IB, LOW);
}
void brakeMotor() {
digitalWrite(IA, HIGH);
digitalWrite(IB, HIGH);
}
void printMl(long p) {
long centi = p * CENTI_ML_PER_TURN / PULSES_PER_TURN;
Serial.print(centi / 100);
Serial.print('.');
if (centi % 100 < 10) Serial.print('0');
Serial.print(centi % 100);
Serial.print(" ml");
}
void setup() {
Serial.begin(115200);
Serial.println("Mokxi Arduino Uno: dosing pump");
pinMode(IA, OUTPUT);
pinMode(IB, OUTPUT);
pinMode(ENC_A, INPUT);
pinMode(ENC_B, INPUT);
pinMode(BUTTON, INPUT_PULLUP);
brakeMotor();
attachInterrupt(digitalPinToInterrupt(ENC_A), onA, RISING);
Serial.print("press for a dose of ");
printMl(DOSE_PULSES);
Serial.println();
}
void dose() {
noInterrupts();
pulses = 0;
interrupts();
Serial.println("pumping");
run(FULL);
bool slow = false;
while (count() < BRAKE_AT) {
if (!slow && count() >= SLOW_AT) {
run(CRAWL);
slow = true;
}
}
brakeMotor();
delay(500);
long p = count();
Serial.print("dosed ");
printMl(p);
Serial.print(" (");
Serial.print(p);
Serial.println(" pulses)");
}
void loop() {
if (digitalRead(BUTTON) == LOW) {
dose();
while (digitalRead(BUTTON) == LOW) delay(10);
delay(50);
}
}
Parts list
6 parts, plus the jumper wires. Every one is in the editor's parts bin.
How it is wired
10 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.
- Arduino Uno R3 pin 6; Motor driver, L9110S pin AIB
- Arduino Uno R3 pin 5; Motor driver, L9110S pin AIA
- Arduino Uno R3 pin 4; Pushbutton pin 1a
- Arduino Uno R3 pin 3; Gear motor with encoder pin B
- Arduino Uno R3 pin 2; Gear motor with encoder pin A
- Arduino Uno R3 pin 5V; Gear motor with encoder pin VCC
- Ground: Arduino Uno R3 pin GND; Pushbutton pin 2a; Motor driver, L9110S pin GND; Gear motor with encoder pin GND; Battery, 9 V, AA or 18650 pin V-
- Motor driver, L9110S pin VCC; Battery, 9 V, AA or 18650 pin V+
- Motor driver, L9110S pin OA1; Gear motor with encoder pin M1
- Motor driver, L9110S pin OA2; Gear motor with encoder pin M2
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.