- Published on
Java Automation Tutorial – Cluckburg Part 2 - Basic Software
- Authors
- Name
- Michal Strzalkowski
- @michalmadethis
First things first, I need to test that the stepper motor works and code up a way to run the motor the correct number of revolutions either direction to open/close the coop door. Here is what I’m focusing on in the road-map:
- Getting the motor to move via code
- Writing testing code to open/close the coop door via stepper.
I refactored and modified the very helpful starter code that Robert Savage posted for his Pi4J library project (http://www.savagehomeautomation.com/projects/raspberry-pi-stepper-motor-control-breakout-board.html). The link is worth reading if only to better understand how the pi headers work.
The first thing needed is to install Pi4J into Raspbian. Unfortunately the ‘easy’ installation did not work for me due to the provided script failing with some timeouts, so I proceeded with manual/offline installation following this guide. This simply involved:
- Getting the latest Pi4J package onto the pi from this link: http://get.pi4j.com/download/pi4j-1.1.deb
- Navigating to the location in a terminal window and executing the following command:
sudo dpkg -i pi4j-1.1.deb
Next I started modifying Robert’s code specifically for opening/closing the coop door and allowing for some calculation of daylight hours. I did this in the out of the box java editor (BlueJ) on the pi since I already had everything hooked up and the wifi dongle I was using was being temperamental – sidenote: When running this code, if you find that the motor intermittently stops before it’s supposed to, consider that the wifi adapter might be drawing too much power during random network activity.
import com.pi4j.component.motor.impl.GpioStepperMotorComponent;
import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class CluckburgControl
{
  private static byte[] weak_but_fast_sequence = new byte[4];
private static byte[] slow_but_strong_sequence = new byte[4];
private static byte[] very_strong_but_very_slow_sequence = new byte[8];
private static GpioStepperMotorComponent motor;
private static int[] average_minute_for_sunrise_by_month = new int[12];
private static int[] average_minute_for_sunset_by_month = new int[12];
//TODO move out to config.
private static boolean IS_DOOR_OPEN = false;
private static int tickIntervalInSeconds = 1800; //30 minutes
private static int revolutions_to_open = 2;
private static int revolutions_to_close = -2;
public static void main(String[] args) throws InterruptedException, IOException
{
boolean exitRequested = false;
boolean DEBUGGING = false;
boolean HARDWARE_TEST = false;
if(args != null && args.length > 0){
if(args[0].equalsIgnoreCase("debug_mode")){
DEBUGGING = true;
}
else if(args[0].equalsIgnoreCase("hardware_test")){
HARDWARE_TEST = true;
}
}
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
SetupMotor();
SetupSequences();
SetupDaylightValues();
//Run application loop.
while(!exitRequested){
if(DEBUGGING){
System.out.println("Ready for input:");
String s = br.readLine();
if(s.equalsIgnoreCase("q")){
exitRequested = true;
}
else if(s.equalsIgnoreCase("open")){
OpenCoopDoor();
}
else if(s.equalsIgnoreCase("close")){
CloseCoopDoor();
}
else if(s.equalsIgnoreCase("lightcheck")){
System.out.println(IsThereLightOut());
}
}
else if (HARDWARE_TEST) {
//Every 15 seconds, open/close the coop door to test gearing/winch/mounting etc of drawbridge.
OpenCoopDoor();
Thread.sleep(15000); //15 seconds
CloseCoopDoor();
Thread.sleep(15000); //15 seconds
}
else {
boolean isThereLightOut = IsThereLightOut();
//If it's daytime and the door has not opened, open it.
if(isThereLightOut && !IS_DOOR_OPEN){
OpenCoopDoor();
}
//IF it's nightime and the door is still open, close it.
else if(!isThereLightOut && IS_DOOR_OPEN){
CloseCoopDoor();
}
Thread.sleep(tickIntervalInSeconds * 1000);
}
}
System.out.println("Exited.");
}
private static void OpenCoopDoor() throws InterruptedException {
System.out.println("Opening Coop Door");
// define stepper parameters before attempting to control motor
// anything lower than 2 ms does not work for my sample motor using single step sequence
motor.setStepInterval(2);
motor.setStepSequence(weak_but_fast_sequence);
System.out.println(" Motor FORWARD with faster speed and lower torque for " + revolutions_to_open + " revolutions.");
motor.rotate(revolutions_to_open);
System.out.println(" Motor STOPPED for 2 seconds.");
Thread.sleep(2000);
// final stop to ensure no motor activity
motor.stop();
IS_DOOR_OPEN = true;
System.out.println("Door should be OPEN");
}
private static void CloseCoopDoor() throws InterruptedException{
System.out.println("Closing Coop Door");
motor.setStepInterval(10);
motor.setStepSequence(slow_but_strong_sequence);
System.out.println(" Motor BACKWARDS with slower speed and higher torque for " + revolutions_to_close +" revolution.");
motor.rotate(revolutions_to_close);
System.out.println(" Motor STOPPED for 2 seconds.");
Thread.sleep(2000);
// final stop to ensure no motor activity
motor.stop();
IS_DOOR_OPEN = false;
System.out.println("Door should be CLOSED");
}
private static void SetupMotor(){
// create gpio controller
final GpioController gpio = GpioFactory.getInstance();
// provision gpio pins #00 to #03 as output pins and ensure in LOW state
final GpioPinDigitalOutput[] pins = {
gpio.provisionDigitalOutputPin(RaspiPin.GPIO_00, PinState.LOW),
gpio.provisionDigitalOutputPin(RaspiPin.GPIO_01, PinState.LOW),
gpio.provisionDigitalOutputPin(RaspiPin.GPIO_02, PinState.LOW),
gpio.provisionDigitalOutputPin(RaspiPin.GPIO_03, PinState.LOW)};
// this will ensure that the motor is stopped when the program terminates
gpio.setShutdownOptions(true, PinState.LOW, pins);
// create motor component
motor = new GpioStepperMotorComponent(pins);
// There are 32 steps per revolution on my sample motor,
// and inside is a ~1/64 reduction gear set.
// Gear reduction is actually: (32/9)/(22/11)x(26/9)x(31/10)=63.683950617
// This means is that there are really 32*63.683950617 steps per revolution
// = 2037.88641975 ~ 2038 steps!
motor.setStepsPerRevolution(2038);
}
}
Next Steps:
- Getting the motor to move via code
- Writing testing code to open/close the coop door via stepper.
- Designing a simple prototype winch system and testing string length/torque against the weight of the door.
- Figuring out how to tell if there is daylight out, triggering opening and closing appropriately
- Deciding on how to power the pi once inside Cluckburg – solar/running a wire to a weatherproof box to also power a heatlamp etc.