Analog Dials as Real Time Monitors

I have a deep love of old style analog dials. Recently I found a place selling old voltage meters. There were unusable by todays standards but they looked great. With a bit customising I set about converting them to use hobby servos and make them fully controlled by an Arduino.

The analog dials:

 

Monitors

 

Below is fairly quick transformation. Gutted the insides of the unit and used a glue gun to mount the servo and blu tac to attach the needle.

 

Monitor in bits Work of Art

Guts Monitor with Servo

 

 

After the servos were mounted, I added a tri colour LED.

Lights

It took a bit effort to get the casing back on the dials especially with the LED bent over the top the dial. Both the dials are mounted on an old shelf.

Screen Shot 2014-12-08 at 22.24.37

Above is the basic wiring, two servo controls and 6 LED controls. I kept the breadboard to make sure I was able to change things later.

Below is the simple code I used to control them:

/* 
I started with the sample from this guy

Sweep
 by BARRAGAN <http://barraganstudio.com> 
 This example code is in the public domain.

 modified 8 Nov 2013
 by Scott Fitzgerald

http://arduino.cc/en/Tutorial/Sweep

*/ 

#include <Servo.h> 
 
Servo output_dial;  // create servo object to control a servo 
Servo input_dial;   // twelve servo objects can be created on most boards
  
boolean stringComplete = false;
String inputString = "";
int led_green_input = 2;
int led_blue_input = 3;
int led_red_input = 4;
int led_green_output = 6;
int led_blue_output = 7;
int led_red_output = 8;

void setup() 
{ 
  Serial.begin(9600);
  input_dial.attach(9);  // left
  output_dial.attach(10); // right
  pinMode(led_green_input, OUTPUT);
  pinMode(led_blue_input, OUTPUT);
  pinMode(led_red_input, OUTPUT);
  pinMode(led_green_output, OUTPUT);
  pinMode(led_blue_output, OUTPUT);
  pinMode(led_red_output, OUTPUT);
  output_dial.write(135); // reset to left side of the dial
  input_dial.write(145); // reset to left side of the dial
  
  // set green as LED colour default
  digitalWrite(led_red_input, LOW);
  digitalWrite(led_blue_input, LOW);
  digitalWrite(led_green_input, HIGH);
  digitalWrite(led_red_output, LOW);
  digitalWrite(led_blue_output, LOW);
  digitalWrite(led_green_output, HIGH);
} 
 
void loop() 
{ 
  
  if (stringComplete) {
    inputString.replace("\n","");
    int len = inputString.length();
    String servo = inputString.substring(0,1);
    String colour = inputString.substring(2,3);
    String movement = inputString.substring(4,len);
    Serial.println(stringComplete);
    
    // limit how far we move the meter to make suer we don't
    // damage the needle on the side of the casing.
    
    // right dial
    if (servo.toInt() == 1 && movement.toInt() <= 135 && movement.toInt() >= 30){
      output_dial.write(movement.toInt()); 
    }
    
    // left dial
    if (servo.toInt() == 0 && movement.toInt() <= 145 && movement.toInt() >= 55){
      input_dial.write(movement.toInt());
    }
    
    if (servo == "1"){
      if (colour.toInt() == 0){
        digitalWrite(led_red_output, LOW);
        digitalWrite(led_blue_output, LOW);
        digitalWrite(led_green_output, HIGH);
      }
      if (colour.toInt() == 1){
        digitalWrite(led_red_output, LOW);
        digitalWrite(led_blue_output, HIGH);
        digitalWrite(led_green_output, LOW);
      }
      if (colour.toInt() == 2){
        digitalWrite(led_blue_output, LOW);
        digitalWrite(led_red_output, HIGH);
        digitalWrite(led_green_output, LOW);
      }
    }
    if (servo == "0"){
      if (colour.toInt() == 0){
        digitalWrite(led_red_input, LOW);
        digitalWrite(led_blue_input, LOW);
        digitalWrite(led_green_input, HIGH);
      }
      if (colour.toInt() == 1){
        digitalWrite(led_red_input, LOW);
        digitalWrite(led_blue_input, HIGH);
        digitalWrite(led_green_input, LOW);
      }
      if (colour.toInt() == 2){
        digitalWrite(led_blue_input, LOW);
        digitalWrite(led_red_input, HIGH);
        digitalWrite(led_green_input, LOW);
      }
    }
    // clear the string:
    inputString = "";
    stringComplete = false;
  }
 
  delay(1000); 
 
} 

void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read(); 
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == '\n') {
      stringComplete = true;
    } 
  }
}

https://gist.github.com/bassdread/952e9df9d820c16841f3 contains a better downloadable gist.

The above code is enough to move the needle the full range of the dial without hitting the side and damaging the needle. It also controls the colour of the LED. Currently it does a basic solid colour without any mixing.

To make it standalone I connected the Arduino to a Raspberry Pi and ran the following simple Flask app to control the dials over serial.

from flask import Flask
import serial
from datetime import datetime, timedelta
from time import sleep

serial_port = '/dev/tty.usbserial-A501S1GX'
ser = serial.Serial(serial_port, 9600)
sleep(1.5)

app = Flask(__name__)

# input is the left side dial
@app.route("/input/<colour>/<movement>")
def input(colour, movement):
    ser.write('0,{0},{1}\n' .format(colour, movement))
    return "Done!"

@app.route("/output/<colour>/<movement>")
def output(colour, movement):
    ser.write('1,{0},{1}\n' .format(colour, movement))
    return "Done!"


if __name__ == "__main__":
    app.run()

https://gist.github.com/bassdread/1a7d49adebf288c4684b is the downloadable gist.

Below is a better quality selection of pictures of it in action.

Pi Wars

This weekend (6th December) I was lucky enough to attend http://piwars.org/ at the Institute of Astronomy, Cambridge. I wasn’t entirely sure what to expect but it was a robotics event outside London so it was worth the risk.

I took my young ish son and a friend on the trip with the promise of robots and hopefully some robot wars style fun. We arrived a little bit early and had time to have a look around and chat to some of the exhibitors and robot owners before the competition kicked off.

The first that struck me was how friendly and happy to talk everyone was. Everyone was there to learn and even if they didn’t realise it, teach. My son was very impressed with http://www.cannybots.com/ these are small 3D printed bluetooth controlled like cars that could with follow a black line on white paper or be controlled manually. My friend and the guy running the display happily traded 3D printing stories while my son and I raced two little robot cars around the table.

All the robots taking part were Pi powered and varied in size. There were a number of different courses and challenges. The assault course below was a good watch with enough varied surfaces to challenge most of the robots.

Assault Course

The drag strip and the sumo fights were also great to watch. Some of the sumo matches ended up being a bit one sided.

 

 

Drag Race Sumo

 

It was a well organised and entertaining event. The boy and I intend to return, hopefully with a robot.

The venue was also very interesting, we went for a quick stroll for lunch and found an awesome old style telescope, well at least the container for it.

Outside

Tornado Testing

Tornado testing can get complex. Below is a simple snippet for testing a POST request that accesses a supplied argument. Took me ages to figure out the arguments needed to be urlencoded. Hope this saves you some time when testing your Tornado code.

from tornado import ioloop
from tornado.testing import AsyncHTTPTestCase
from tornado.web import Application, RequestHandler
import urllib


class MainHandler(RequestHandler):
    def get(self):
        self.write("Hello, world")

    def post(self):
        name = self.get_argument('name')
        self.write("Hello, {0}".format(name))


class TestHelloWorld(AsyncHTTPTestCase):

    def get_app(self):
        return Application([(r"/", MainHandler)])

    def test_hello_world(self):
        # add any useful header stuff here
        headers = {}

        data = {'name':'Tony'}
        body = urllib.urlencode(data)

        self.http_client.fetch(
            self.get_url('/'),
            self.stop,
            method="POST",
            body=body,
            headers=headers
        )
        response = self.wait()
        self.assertEquals(response.body, 'Hello, Tony')


if __name__ == "__main__":
    application = Application([
        (r"/", MainHandler),
    ])
    application.listen(8888)
    ioloop.IOLoop.instance().start()