Spinning the wheels - Witch way and how fast?
A clip from the installation Color & Sound - Valve Cluster 1 where I needed 6 rotary encoders on an Arduino.
I came up with a very simple way to make rotary encoders. I needed a "turn the handle" interface for an installation. Usually, this can be solved with either a potmeter or a really fancy precision rotary encoder.
But both have drawbacks. Potmeters need "end stops" to block overturning them.
Fancy rotary encoders need high sampling rates, and take up several legs on the arduino each.
But: What spins and are good at it: A DC motor. Reversing it and using it as a generator, you suddenly have a simple way of knowing "Wich way are they turning the wheel, and how fast".
You need
- A small DC motor (permanent magnet type)
- A resistor (10-20K)
- An Arduino Board
Connect one terminal of the DC motor to the 3.3 V on the Arduino board
Connect the other terminal to an analog input.
How it works
| Analog input value | ||
| No rotation | The 3.3 V "flow" right through the windings of the DC motor | 676 (1024/5*3.3) |
| Spin one way | The DC motor acts as a generator, making maybe + 1 V (depending on the speed of your rotation). This is "added" to the + 3.3 V = 4.3 V | 880 (> 676 : one way) |
| Spin other way | DC motor makes - 1 V. This gets "subtracted" from the +3.3V = 2.3 V |
471 (<676 : other way) |
The circuit
Considerations
- ? Why not make 2.5 volt with a voltage divider and feed that to the DC motor, to get the zero-rotation to read 512 on the analog input?
- ! Because you get bonus points for solving a problem with as few extra parts as possible.. :-)
- ? Why the 15K resistor?
- ! If the DC motor is spun really fast, the "overpower" to the Arduino Board makes it shut down. The resistor prevent this.
The code
This is the code from the Color & Sound installation. It uses the DC moters as encoders, and outputs DMX to control lights.
#include #include <DmxSimple.h> /* Christian Liljedahl, 2010. christian.liljedahl.dk christian@liljedahl.dk Example of how to use 6 DC motors as rotary encoders for Arduino This code returns a position on a circle (CirclePosition) from 0.0 to 1.0 If the DC motor is not rotating, the CirclePosition is not moving either Once the DC motor is turned one way, CirclePosition starts moving up (or down). The speed depends on how fast the encoder (DC motor) is rotated. Please note: CirclePosition is not the position of the axis of the DC motor. There is no way (to my knowledge) this setup can give you the position of the DC motor. Only: Are we turning clockwise or counterclockwise */ // Arrays to keep readings int AnalogInReading[6]; int AnalogZeroPoint[6]; double RotationSpeed[6]; double CirclePosition[6]; void setup() { // DMX stuff - This program controlls colors. DmxSimple.maxChannel(30); //Serial Serial.begin(9600); // Make a reading readAllAnalog(); // Set zeropoints calibrate(); // Set starting point for cirle initPositions(); } long time = 0; void loop() { // read the value from the sensor: readAllAnalog(); calculateRotationSpeed(); changeCirclePosition(); //FictionalRotationValues(); makeHueAndSendDmx(); } void setDmxValues(byte rgb[], int startchannel){ DmxSimple.write(startchannel, rgb[0]); startchannel++; DmxSimple.write(startchannel, rgb[1]); startchannel++; DmxSimple.write(startchannel, rgb[2]); startchannel++; DmxSimple.write(startchannel, 255); } void readAllAnalog(){ for(int i=0;i<6;i++){ AnalogInReading[i] = analogRead(i); } } void calibrate(){ for(int i=0;i<6;i++){ AnalogZeroPoint[i] = AnalogInReading[i]; } } void initPositions(){ for(int i=0;i<6;i++){ CirclePosition[i] = 0.5; } } void calculateRotationSpeed(){ for(int i=0;i<6;i++){ RotationSpeed[i] = AnalogZeroPoint[i] - AnalogInReading[i]; } } void changeCirclePosition(){ for(int i=0;i<6;i++){ // What way do we rotate double rotFactor = (RotationSpeed[i]*RotationSpeed[i]-400)/1000000; if(RotationSpeed[i] > 20){ // How fast do we rotate CirclePosition[i] += rotFactor; // Wrap around if(CirclePosition[i] > 1.0) CirclePosition[i] = 0.0; } if(RotationSpeed[i] < -20){ CirclePosition[i] += -rotFactor; // Wrap around if(CirclePosition[i] < 0.0) CirclePosition[i] = 1.0; } } } void makeHueAndSendDmx(){ // Dmx start address of first fixture int channel = 1; for(int i=0;i<6;i++){ byte colors[3]; // We use the circlepositoin directly for Hue, since CirclePosition is 0.0-1.0 double Hue = CirclePosition[i]; // Make sure we don't get an overflow issue Hue = constrain(Hue, 0.0, 1.0); // Convert the CirclePosition-Hue to RDRB for the dmx HSL2RGB(colors, Hue, 1.0, 0.6); // Send the colors to the fixture setDmxValues(colors, channel); // Move to next fixture channel += 4; } } // Given H,S,L in range of 0-1 // Returns a Color (RGB struct) in range of 0-255 void HSL2RGB(byte rgb[], double h, double sl, double l) { double v; double r,g,b; r = l; // default to gray g = l; b = l; v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl); if (v > 0) { double m; double sv; int sextant; double fract, vsf, mid1, mid2; m = l + l - v; sv = (v - m ) / v; h *= 6.0; sextant = (int)h; fract = h - sextant; vsf = v * sv * fract; mid1 = m + vsf; mid2 = v - vsf; switch (sextant) { case 0: r = v; g = mid1; b = m; break; case 1: r = mid2; g = v; b = m; break; case 2: r = m; g = v; b = mid1; break; case 3: r = m; g = mid2; b = v; break; case 4: r = mid1; g = m; b = v; break; case 5: r = v; g = m; b = mid2; break; } } //ColorRGB rgb; rgb[0] = byte(r * 255.0f); rgb[1] = byte(g * 255.0f); rgb[2] = byte(b * 255.0f); } void dumpAnalogReadings(){ for(int i=0;i<6;i++){ Serial.println(AnalogInReading[i]); } Serial.println("---"); } void FictionalRotationValues(){ RotationSpeed[0] = 0; RotationSpeed[1] = 100; RotationSpeed[2] = 200; RotationSpeed[3] = 300; RotationSpeed[4] = 400; RotationSpeed[5] = 500; }


