Monday, 30 March 2015

Thermo-Bot Project




In this post, I describe how I took a stepper motor, some Lego(R) and a temperature probe to make a temperature sensitive robot. Basically it's a Pi-on-wheels that moves to the correct position on a temperature scale to indicate the current room temperature. Don't ask why I thought this was a good idea, but I'm quite proud that it works!



Video of it working here (time lapse of 50 minutes) https://youtu.be/bbkwAPPF_vY


I bought a stepper motor with a view to making some kind of robot.

This tutorial from Simon Monk on the Adafruit site is all you need for Pi-controlled stepper motors:

https://learn.adafruit.com/adafruits-raspberry-pi-lesson-10-stepper-motors

I bought my stepper motor from ebay with the controller chip included on a board with some LEDs, which were really helpful in debugging, since the wires in the Adafruit diagram don't 'line up' obviously with the Pi GPIO pins. My motor would only go forwards until I followed the diagram MUCH more carefully. (The fault was with me not Simon's diagram.)

I also did some work with the single wire temperature sensor, again using the Adafruit tutorial from Simon Monk:

https://learn.adafruit.com/adafruits-raspberry-pi-lesson-11-ds18b20-temperature-sensing

Although some of the Python code came from here:

http://www.modmypi.com/blog/ds18b20-one-wire-digital-temperature-sensor-and-the-raspberry-pi:


However, this time it wasn't my fault when it didn't work. With model B+ Pis you have to add this to the /boot/config.txt file:

dtoverlay=w1-gpio,gpiopin=4

Once this was in place the sensor worked fine.

The robot is cobbled together really quickly (badly) using lego. Initial belt-drive version had problems with the elastic-band drive belt jumping off the drive wheel, so direct drive was used instead.

Then it was time to combine the two tutorials and with a few extra lines of code I have a robot that moves up and down a temperature scale according to the current room temperature.


Here's the code I used. You'll notice most of it is pretty much left as per the Adafruit example and the modmypi link above, so credit to Simon Monk and the ModMyPi guru.



import RPi.GPIO as GPIO
import time
import os

#initialise bot settings (stepper motor))
GPIO.setmode(GPIO.BCM)
 
enable_pin = 18
coil_A_1_pin = 7
coil_A_2_pin = 8
coil_B_1_pin = 23
coil_B_2_pin = 24
 
GPIO.setup(enable_pin, GPIO.OUT)
GPIO.setup(coil_A_1_pin, GPIO.OUT)
GPIO.setup(coil_A_2_pin, GPIO.OUT)
GPIO.setup(coil_B_1_pin, GPIO.OUT)
GPIO.setup(coil_B_2_pin, GPIO.OUT)
 
GPIO.output(enable_pin, 1)
 
delay = 2

def forward(delay, steps):
 for i in range(0, steps):
  setStep(1, 0, 1, 0)
  time.sleep(delay)
  setStep(0, 1, 1, 0)
  time.sleep(delay)
  setStep(0, 1, 0, 1)
  time.sleep(delay)
  setStep(1, 0, 0, 1)
  time.sleep(delay)
 
def backwards(delay, steps):
 for i in range(0, steps):
  setStep(1, 0, 0, 1)
  time.sleep(delay)
  setStep(0, 1, 0, 1)
  time.sleep(delay)
  setStep(0, 1, 1, 0)
  time.sleep(delay)
  setStep(1, 0, 1, 0)
  time.sleep(delay)
 
def setStep(w1, w2, w3, w4):
 GPIO.output(coil_A_1_pin, w1)
 GPIO.output(coil_A_2_pin, w2)
 GPIO.output(coil_B_1_pin, w3)
 GPIO.output(coil_B_2_pin, w4)

#from http://www.modmypi.com/blog/ds18b20-one-wire-digital-temperature-sensor-and-the-raspberry-pi:
#load drivers for temperature probe
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')

temp_sensor = '/sys/bus/w1/devices/28-0414607111ff/w1_slave'

#Get the raw data from the sensor:

def temp_raw():

 f = open(temp_sensor, 'r')
 lines = f.readlines()
 f.close()
 return lines

def read_temp():

 lines = temp_raw()
 while lines[0].strip()[-3:] != 'YES':
  time.sleep(0.2)
  lines = temp_raw()
        
 temp_output = lines[1].find('t=')

 if temp_output != -1:
  temp_string = lines[1].strip()[temp_output+2:]
  temp_c = float(temp_string) / 1000.0
  return temp_c

#initialise current temperature start value (and place bot on 14 deg C mark before running)
currentTemp = 14.00


#run the bot
while True:
  oldTemp = currentTemp
  currentTemp = read_temp()
  tempDiff = currentTemp - oldTemp
  print (oldTemp, currentTemp, tempDiff,)
  if tempDiff >= 0:
   steps = tempDiff * 256
   forward(int(delay) / 1000.0, int(steps)) 
  else:
   steps = tempDiff * -256
   backwards(int(delay) / 1000.0, int(steps))
  setStep(0,0,0,0)
  time.sleep(60)
  
 
It works!

Tuesday, 10 March 2015

Pi-based Domestic Electricity Monitor - Part 6 - First Log - Success and Failure

Summary:

I logged 24hrs of data from my domestic electricity meter, but discovered that writing each event to the db with SQlite seemed to be taking 4 seconds, making the program as it stands no good for logging an event which can happen more than twice a second on occasions of high consumption. Values below 1kW were recorded accurately I believe.

To run the program via SSH I used nohup for the first time so that it would run even once I closed the session...and then had to learn how to find and kill said program:

To hunt running program: 

$ ps -e | grep python


To kill running program:

$ sudo kill 3861


Detail:

I used the program below to log the meter for around 24 hrs

(House averages 1kW, or 800 flashes per hour. 24hrs is therefore around 20,000 flashes)

This was to test the logging process and to see the size of the data file that would be created.

However, on analysing the data I soon noticed the data was suspect:
a) not enough readings for 24hrs (~9000, rather than ~20,000)
b) readings which never go above around 1kW:


(graph created in Libre office after exporting data from sqlite. This site was helpful here: http://www.sqlite.org/cli.html)


 Here's a sample of the data: (Time|Consumption|Meter Reading)

Tue Mar 10 07:23:49 2015|0.980246908963771|16791
Tue Mar 10 07:23:54 2015|0.939329897684513|16791
Tue Mar 10 07:23:58 2015|0.978048032653337|16791
Tue Mar 10 07:24:03 2015|0.977999583707987|16791
Tue Mar 10 07:24:08 2015|0.959483188529128|16791
Tue Mar 10 07:24:12 2015|0.955398819345674|16791
Tue Mar 10 07:24:17 2015|0.97396251715844|16791


Every interval is around 4 seconds and 1kW at a time when it should be well above this.

When I remove the lines that write the data to the db, the consumption data becomes correct, so I think that writing the data to the db is taking around 4 seconds.

The size of the data file is not going to be a problem: ~9000 readings is ~500kB.

Here's the code I used:

It features a facility for calculating the meter reading based on initial values and 800 flashes of the LED being 1kWh.


#!/usr/bin/env python
     
#First go at logging data from the meter

import RPi.GPIO as GPIO, time, os
import sqlite3


#initiaise database tools
conn = sqlite3.connect('elecmeter.db')
c =  conn.cursor()

#create database
#what to send to db: ascitime, power consuption, meter reading
c.execute("CREATE TABLE e_meterlog01 (time float, power float, meter_reading integer)")

#initialise pins     
DEBUG = 1
GPIO.setmode(GPIO.BCM)
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)



#procedure to wait for LED flash front edge
def wait_for_flash():
    try:
        GPIO.wait_for_edge(23,GPIO.RISING)
    except KeyboardInterrupt:
        GPIO.cleanup()       # clean up GPIO on CTRL+C exit 
        conn.close() 
 
#initialise meter reading tools 
meter_rdg = 16777
impulse_count=0

#timing and kW calcs based on 800 imp / kWh:
 
start_time=time.time()

for i in range (0,22000):
    wait_for_flash()
    elapsed_time = time.time()-start_time
    start_time=time.time() 
 
#increment the meter reading every 800 pulses:
 
    if impulse_count < 800:
        impulse_count += 1
    else:
        meter_rdg +=1
        impulse_count=0 
 
    kW=((1/elapsed_time)*4.5) 
 
    print (elapsed_time,kW,meter_rdg,impulse_count)
    c.execute("INSERT into e_meterlog01 VALUES(?,?,?)",(time.asctime(),kW,meter_rdg))
    conn.commit()
    time.sleep(0.3) #to allow for LED to turn off
    
GPIO.cleanup()           # clean up GPIO on normal exit        
conn.close()

print("Ending Program")


Saturday, 7 March 2015

Remote accessing a Raspberry Pi

Just a few tweeks to the pi to make it easier to work on it remotely:

1) Autostart the VNC server - https://learn.adafruit.com/adafruit-raspberry-pi-lesson-7-remote-control-with-vnc/running-vncserver-at-startup (EDIT - I had to turn this off. After the first time of working, thereafter the remote screen was just grey with an 'x' mouse pointer. Starting manually works fine)

2) Get LAMP installed to allow remote access: http://elinux.org/RPi_A_Simple_Wheezy_LAMP_install

3) Install and get no-ip working -http://www.noip.com/support/knowledgebase/installing-the-linux-dynamic-update-client/

It all works: http://jcwyatt.ddns.net

However I installed no-ip using some other instructions, which I won't link to, which put the noip client in the home directory, which I think is why I can't start noip the same way as I start TightVNC. It needs root privileges to run noip now, and I don't think it should if you install it properly.

(EDIT - My mobile network provider is blocking access to this site, but I can access it from everywhere else I've tried. Will try changing the re-direct settings on my router and the no-ip settings.)


Next task is writing the data continually to a sqlite db for 24 hours, to gauge the file size.

Tuesday, 24 February 2015

Pi-based Domestic Electricity Monitor - Part 5 - 'Connecting' the LDR + RPi to the meter

In this post, I've connected the Raspberry Pi to the electricity meter and created a python program to work out the consumption rate:

With the Raspberry Pi and the LDR sensor able to detect changes in light levels, it was time to 'connect' the LDR to the electricity meter's LED. Since I've read that you are not permitted to tamper with the meter at all, this consisted of taping the LDR to the front of the meter with insulating tape. (Image to follow). Should hold long-term, but easy to remove.

The meter's LED flashes 800 times for 1 kWh. From the interval between flashes, the instantaneous rate of consumption can be calculated. I made it (1/interval x 4.5). See working out below.


I made a few errors along the way, by waiting for the LED to go off but not factoring in this wait time, and messing up my maths the first time round.

Here's the output. The first colomn is the interval between flashes in seconds, the second is the power consumption in kW:


That's if my code is correct:





#!/usr/bin/env python

#First go with the LDR picking up the flashes from the electrcity meter.

import RPi.GPIO as GPIO, time, os

#procedure to wait for LED flash front edge
def wait_for_flash():
    try:
        GPIO.wait_for_edge(23,GPIO.RISING)
    except KeyboardInterrupt:
        GPIO.cleanup()       # clean up GPIO on CTRL+C exit 

#initialise pins     
DEBUG = 1
GPIO.setmode(GPIO.BCM)
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)


#timing and kW calcs based on 800 imp / kWh

start_time=time.time()

for i in range (0,20):
    wait_for_flash()
    elapsed_time = time.time()-start_time
    start_time=time.time()
    kW=((1/elapsed_time)*4.5)
    print (elapsed_time,kW)
    time.sleep(0.3) #to allow for LED to turn off

GPIO.cleanup()           # clean up GPIO on normal exit        

print("Ending PRogram")



The next steps are to write values to a database with SQLite:
current consumption rate to 3 d.p., date and time, calculated meter reading as a check of accuracy against actual meter reading.

Then I would like to have a new program to process and analyse the data.




Monday, 23 February 2015

Debugging a lesson on Debugging - Mobile Mistakes #1

iPads are wonderful devices. No really, they are. Even if they make you start a sentence without a capital letter.

This post however, is about what can go wrong in an IT lesson using mobile devices and the lessons learned.

I am working with a school that has amazing IT provision, with one-to-one across the board: iPads in LKS2 and Macbooks in UKS2, plus a suite of networked machines. I have occasional use of an iPad, so I borrowed one with 'Hopscotch' installed, to plan my Computer Science lesson on debugging for a year 3 class.

I put together what I hoped was a well-structured plan for making a tilting 'etch-a-sketch' game, with debugging being the focus, but with some cross-curricular maths work on angles included.

However, what I did not plan for was that within the class set of iPads, two different versions of hopscotch were installed, neither of which were the same as the one I had used to plan with!

You would not think that this would be so significant for something as simple as a tilting 'etch-a-sketch' game. However, what if one version of Hopscotch required using angles to set the direction of motion of the sprite, and the other used +ve and -ve co-ordinates for the same task? Some advice: don't try teaching angles to 360° and cartesian co-ordinates and negative numbers to 2 sections of a Year 3 class simultaneously. It doesn't go well.

Despite this, and the fact that it was the last hour of the last day of half term, and that there'd been a cake sale immediately prior to the lesson, the children were great. Most of the 'angle' children succeeded, and some of the x-y group got something working. But was it a great lesson on debugging? Only for me.

Lessons learned:

  • Check all the mobile devices you are going to use have the software you want installed.
  • Check it is the same version on all devices.
  • Plan your lesson using the same device the children will use (or at least the same software)






Saturday, 21 February 2015

Pi-based Domestic Electricity Monitor - Part 4 - Timing intervals between flashes of the LED

The next stage in developing the electricity monitor, using the flashing LED on my domestic meter and an LDR connected to the PI, is to try to detect when the LED is on / off and log the time between flashes.

Hardware changes:

Instead of using an RC circuit, I swapped the capacitor for a 1kΩ resistor, as I am no longer interested in the analogue light level, just whether a light input is on or off. (Diag. to follow once I get Fritzing working!) [edit - Fritzing is cool]




I also added a mini voltmeter I've had kicking around for ages from a previous project, so that I could see what was happening to the voltage level on pin 23 for debugging.

Software

This was very helpful: http://raspi.tv/2013/how-to-use-interrupts-with-python-on-the-raspberry-pi-and-rpi-gpio

I had an 'unusual' set up for testing, in that I was having a lie-in on a Saturday morning. I could control the blinds in my room from the bed, and that was enough to set Pin 23 high or low.

here's the code and output. It still needs a few tweaks but it works well enough for testing. Next job is to rebuild the hardware and attach it to the electricity meter.





#!/usr/bin/env python
     
# Example for edge sensing on pin 23 using a LDR and Resistor in series

#thanks to Alex Eames @ http://raspi.tv for the useful hints

#Outline:
#1)set pin low
#2)wait for falling edge
#3)start timer
#4)Wait for next falling edge
#5)stop timer and log or print time since last event
#6)reset and restart timer
#7) repeat
     
import RPi.GPIO as GPIO, time, os

#procedure to wait for transit light-->dark: 
 
def wait_for_dark():
    try:
        GPIO.wait_for_edge(23,GPIO.FALLING)    
    except KeyboardInterrupt:
        GPIO.cleanup()       # clean up GPIO on CTRL+C exit 

#initialise pins:     
DEBUG = 1
GPIO.setmode(GPIO.BCM)
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)


wait_for_dark()

#timing
for i in range (0,5):
    start_time =time.time()
    wait_for_dark()
    elapsed_time = time.time()-start_time
    print (GPIO.input(23),elapsed_time)
    time.sleep(2)

GPIO.cleanup()           # clean up GPIO on normal exit        

print("Ending PRogram")  

Monday, 16 February 2015

Pi-based Domestic Electricity Monitor - Part 3 - Writing data to a file

Warning! This post is just my rough notes as I learn how to do this, so I can find the info again. It may not be as cohesive as I would like, in the short term. I'll tidy it up when I'm done, honest I will.  

Now I can collect the data from the light sensor, I need to be able to write it to a database file.

Looking around, SQLite would seem to be a good choice for this. It's lighter weight than MySQL and, I hope, easier to work with. Also, it's recommended for local file hosting, and since the data will always live on the Pi, that's OK.

I wasted too much time trying to install SQLite, before I discovered ITS ALREADY INCLUDED WITH PYTHON!

I think I'll be needing some help with using it, so here's a tutorial or two I'll use

https://docs.python.org/3/library/sqlite3.html

http://www.tutorialspoint.com/sqlite/sqlite_installation.htm



Time-handling info:

https://docs.python.org/3/library/time.html


Before I start using the light sensor to detect the little flashing light on my electricity meter, I want to test writing to a database with live light level data. Which I'm off to try next...

(Later that same day...)

...I captured sunset light levels tonight! (My daughters went into the room to watch TV though, and turned the lights on). Actual sunset is listed as 5:31pm.


The chart was made by copying and pasting the raw text data from the screen to LibreOffice Calc and post-processing it, so that it went down and not up as the sun went down. Raw values for 'light level' went from about 1000 (bright) to 700,000(dark) as the capacitor took longer to charge in the dark.

 Here's the code I used:

#!/usr/bin/env python
     
# Example for RC timing reading for Raspberry Pi
     
import RPi.GPIO as GPIO, time, os
import sqlite3

#initiaise database tools
conn = sqlite3.connect('lightlevels.db')
c =  conn.cursor()

#create database
#c.execute("CREATE TABLE lightlevel (data text, level integer)")



     
#read light level from GPIO
DEBUG = 1
GPIO.setmode(GPIO.BCM)
  
def RCtime (RCpin):
    reading = 0
    GPIO.setup(RCpin, GPIO.OUT)
    GPIO.output(RCpin, GPIO.LOW)
    time.sleep(0.1)
     
    GPIO.setup(RCpin, GPIO.IN)
    # This takes about 1 millisecond per loop cycle
    while (GPIO.input(RCpin) == GPIO.LOW):
        reading += 1
    return reading
     
for i in range (0,90):
    lilev= (RCtime(23)) # Read RC timing using pin #23
    print (lilev) 
    print (time.asctime())
    c.execute("INSERT into lightlevel VALUES(?,?)",(time.asctime(),lilev))
    conn.commit()
    time.sleep(60)

conn.close()


Not a bad day's work play.

Next up, logging flashes of a LED, and converting the interval to a kW consumption value.


PS
I found these commands here which could be very useful later:


  • gpio wfi <pin> rising/falling/both
This causes GPIO to perform a non-busy wait on a single GPIO pin until it changes state to that indicated.


  • gpio edge <pin> rising/falling/both/none
This enables the given pin for edge interrupt triggering on the rising, falling or both edges. (Or none which disables it)