Latest Work...

Harry Potter / Weasley Clock Part III - Coding

I've lumped all the code for each part of the project together on this page. It's no great elegant work but it gets the job done!

Raspberry Pi:

As I mentioned earlier - all the programming on the Raspberry Pi is done in Python. Sure - it has quite a serious overhead of being interpretted, especially on an embedded platform like the Pi, but in this situation it's hardly a dealbreaker!

 

#! /usr/bin/python

import urllib2,time
import RPi.GPIO as GPIO

#Setup the GPIO pins on the RasPI
#Make 23 and 24 outputs

GPIO.setmode(GPIO.BOARD)
GPIO.setup(23,GPIO.OUT)
GPIO.setup(24,GPIO.OUT)

#Get the time, and make a URL. We use this so as to be sure that the result isn't
#cached somewhere.
timenow = str(time.clock())
urlstring = "http://www.a-barber.com/path/to/php-script.php/?rand="+timenow

#Request the URL - and see what the server says...
response = urllib2.urlopen(urlstring)
command = response.read()

#The server is responsible for determining if the clock has already
#gone to the position. If there's a position that hasn't been picked up
#already by the pi - then the response will be a number followed by 'N'

if 'N' in command:
	#Parse the number (1st char in string) to an integer.
	number = int(command[:,1])
	
	#0 is a valid position (0 steps along from when the 'full rotation' 
	#microswitch is hit) - so send the signal to the Arduino high outside
	# of the loop.
	
	GPIO.output(23, GPIO.HIGH)
	
	for i in range(number):
		#Output a short pulse (off -> on -> off) on the second
		#gpio pin...
		
		GPIO.output(24, GPIO.HIGH)
		
		sleep(0.5) #The timing is not important so this doesn't need to be too 
					# accurate. 
		
		GPIO.output(24, GPIO.LOW)
		
		sleep(0.5)
	#End of the loop 0 or more pulses have been sent...
	
	GPIO.output(23, GPIO.LOW)

#There was no 'N' in command if this has been reached without pulsing. End of the
#script!


 

Arduino

Now we need the Arduino to play nice. This part is much more complex, but here it is. I'll pick out the useful parts after the listing:

 

#include <servo.h>

Servo myservo;

int servo_power = 0;
int servo_pwm   = 1;

int sixth_rotation_in = 7;
int full_rotation_in  = 8;

int data_flag_in = 2;
int data_sig_in = 3;



int servo_speed = 86;
void setup()
{
  pinMode(servo_power,OUTPUT);
  pinMode(sixth_rotation_in,INPUT);
  pinMode(full_rotation_in, INPUT);
  
  pinMode(data_flag_in, INPUT);
  pinMode(data_sig_in, INPUT);
  
  digitalWrite(sixth_rotation_in,HIGH);
  digitalWrite(full_rotation_in,HIGH);
  
  myservo.attach(servo_pwm);
  myservo.write(86);
  

}
void loop()
{
 int stepstotake = getData();

 digitalWrite(servo_power,HIGH);  

  goToStart();
  countSteps(stepstotake);
  digitalWrite(servo_power,LOW);
  
}

void goToStart()
{
  while(1)
  {

    //Rotate the servo until we hit the full rotation
    if(digitalRead(full_rotation_in) == LOW)
    {
      //We've reached the point at the start of the rotation.
      //Now, we need to go one more click on the sixth_rotation before we start counting.
      //It could be that the sixth is pressed and we need to get off it, or we haven't quite reached it
      //and we need to get past it.
      //Serial.write("Full rotation low. \n");
      if(digitalRead(sixth_rotation_in) == HIGH) //We're not yet on it...
      {
        Serial.write("Sixth rotation high. \n");
        while(digitalRead(sixth_rotation_in) == HIGH) //Do nothing while we let it roll on
        {
          delay(50); //Debounce a bit
        }
      }
      if(digitalRead(sixth_rotation_in) == LOW) //Wer'e on it...
      {
            Serial.write("Sixth rotation low. \n");
        while(digitalRead(sixth_rotation_in) == LOW) //Do nothing while we roll off it
        {
          delay(50); //Likewise
        }
      }

       if(digitalRead(sixth_rotation_in) == LOW)
      { 
         Serial.write("Sixth rotation is still low...\n");
      }
      else
      {
         Serial.write("Sixth rotation is high... \n");
      }
 
        //That's all folks!
        return;
    }
  // Not even reached the full rotation yet. Keep on going!
  }
}

int getData()
{
 // Serial.write("Waiting for data...\n");
  while(digitalRead(data_flag_in) == LOW)
  {
    delay(10);
  }
  int dataCounter = 0;
  //Serial.write("Data flag... \n");
  while(digitalRead(data_flag_in) == HIGH)
  {
      while(digitalRead(data_sig_in) == LOW)
      {
        delay(10); 
        if(digitalRead(data_flag_in) == LOW) return dataCounter;
      }
      dataCounter++;
      //Serial.write("Signal... \n");
      while(digitalRead(data_sig_in) == HIGH)
      {
        delay(10);
        if(digitalRead(data_flag_in) == LOW) return dataCounter;
      }
  }
  //Serial.write("Data flag removed...");
  return dataCounter;
}


void countSteps(int steps)
{
  int counter = 0;
  myservo.write(servo_speed);
  while(counter < steps)
  {
      //While it's high do nothing 
       while(digitalRead(sixth_rotation_in) == HIGH)
       {
         delay(50);
       }
       //It went low! Wait for it to go high again...      
       //Now wait for it to go low again 
       counter++;
      
       while(digitalRead(sixth_rotation_in) == LOW)
        {
          delay(50);
        }
        // Now it's back high - that's one pulse! 
        
   }
   return;
}

Even though this mass of code looks a bit daunting - there's very little to it really. The setup() method deals mainly with setting up the Arduino's servo library in order to drive the servo. We then simply turn on and off the power to it as needed. The loop then calls getdata() which, aptly, gets data from the RasPi using the protocol I described earlier of counting how many times it gets a pulse on the one pin whilst another is high. Importantly to note if you're following the code is that this method blocks the Arduino - i.e. stops the loop() from continuing whilst it's waiting for data. Finally, you'll notice that instead of just waiting for stuff to go high I wait for the pins to change state and put a delay in this. This is to do with the bouncing phenomenom I mentioned earlier. Not sure if this is the best way to handle such things as there is the arduino bounce library that probably takes care of this in a much more elegant way.

 

 

Server Code

This is the bit I really want to make some headway on changing. Currently it's two very simple PHP scripts - one that updates a file when I log in and change the clock position, and one that is called by the clock's RasPi every minute by the Python script. 

This is the first one that writes the value to a file on the webserver. This script is very basic - the login session parameter is handled by another script that is accessed prior to this one - and also contains a form to submit the position to this script with.

 

<?php
	session_start();
	if(isset($_SESSION['loggedin']) == FALSE || $_SESSION['loggedin'] == FALSE)
	{
		die('You are not logged in');
	}
	
	$position = $_POST['pos'];
	
	$posFile = "client/position.txt";
	$fh = fopen($posFile,'w');
	
	if($fh == FALSE)
	{
		die("Did not open the file for writing...");
	}
	$pos_str = strval($position);
	
	$pos_str = $pos_str."N" ;
	
	fwrite($fh,$pos_str);
	fclose($fh);
	
	echo "Successfully updated!";
	echo "<a href='dashboard.php'> Back... </a>";
	?>	

Following is the script that is called by the python script on the Raspberry Pi every minute. I log the time of the last request in a file so as to see if the clock's still working on not...

 

<?php
	$position_file = ('position.txt');
	$file_handler = fopen($position_file,'r');
	
	$last_ip_str = 'lastip.txt';
	$last_ip_handler = fopen($last_ip_str,'w');
	
	fwrite($last_ip_handler,$_SERVER['REMOTE_ADDR']);
	fclose($last_ip_handler);

	$file_str = fread($file_handler,10);
	fclose($file_handler);
	
	if(strpos($file_str,'N'))
	{
		$file_handler = fopen($position_file,'w');
		// The position is new! It isn't now though cause it's been picked up.
		// Also assume that any SSH requests have been picked up with it.
		$position = substr($file_str,0,1);
		fwrite($file_handler,$position);
		fclose($file_handler);

	}	
	echo $file_str;
	
	$checkin_time_file = 'lastcheckin.txt';
	$checkin_fh = fopen($checkin_time_file,'w');
	
	fwrite($checkin_fh, date('l jS \of F Y h:i:s A'));
	fclose($checkin_fh);
	
?>

And that's it for the 3 code parts that are needed! 

Up next - bringing it all together, circuits and glue...

 

comments powered by Disqus

<< Go back to the previous page