How to instantly replace existing Raspberry Pis by the ESP8266

For my outdoor sensors I used an RaspberryPi with BMP180 + TSL2561 via I2C transferring the sensor data via MQTT messages.

During an IoT Meetup Session at codecentric office in Frankfurt we discussed the usage of an esp8266 and I was thrilled to get one and play with it.

Adafruit HUZAAH ESP8266 breakout highlights

  • 80-MHz-Mikrocontroller
  • Wi-Fi Direct (P2P), soft-AP
  • Integrated TCP/IP protocol stack with DNS Support
  • Wake up and transmit packets in < 2ms
  • 4 MB Flash-Chip
  • Onboard-Antenna
  • Status LEDs
  • Reset button
  • User button that can also put the chip into bootloading mode,
  • I/O:
    • 1 x Analog input (1.8V max)
    • 9 x GPIO (3.3V logic), which can also be used for I2C or SPI
    • 2 x UART pins
    • 2 x 3-12V power inputs, reset, enable, LDO-disable, 3.3V output

It turns out very quickly that the esp8266 is a really great piece of hardware and I planned to replace my Raspberry Pis acting as sensor hosts in my network. With its wifi capabilities the esp8266 lets me still run the I2C sensors with my MQTT approach.

E. g. the current Raspberry Pi installed outdoors sends values to these topics

  • sweethome/sensors/outdoor/temperature
  • sweethome/sensors/outdoor/pressure
  • sweethome/sensors/outdoor/lux

and if get the esp8266 to publish to these topics too the rest of my network won’t event recognize there was a change…

The Plan: Keep the I2C sensors but replace the “brain”.

replace_this by_that
Raspberry Pi HUZAAH ESP8266 breakout
40€ 10€
SD-Card: 4€
WLAN-Stick 8€
Total: 52€ 10€
Power Consumption: 3W 1W

Hardware

IDE

Arduino IDE

Libraries

The Libraries almost all there, can be easily installed via the Arduino IDE Library Manager.
Except for the esp8266 support the an additional Board Manager URL has to be set:
http://arduino.esp8266.com/stable/package_esp8266com_index.json

prefs_additional_board_managers_url

  • ESP8266WiFi
  • Adafruit BMP085 Unified
  • Adafruit TSL2561 Unified
  • Adafruit MQTT Library

library_manager_esp8266

Software

The esp8266 breakout has a build-in support for LUA, but I decided to flash it to be ready for usage with Arduino IDE and C++ (mostly because the drivers are all well supported by Adafruit).

#include <ESP8266WiFi.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP085_U.h>
#include <Adafruit_TSL2561_U.h>

/************************* WiFi Access Point *********************************/

#define WLAN_SSID       "Lummerland"
#define WLAN_PASS       "****************"

/************************* MQTT Broker Setup *********************************/

const int MQTT_PORT = 1883;
const char MQTT_SERVER[] PROGMEM    = "192.168.0.61";
const char MQTT_CLIENTID[] PROGMEM  = "ESP-PUBLISHER-SERVICE";
const char MQTT_USERNAME[] PROGMEM  = "********";
const char MQTT_PASSWORD[] PROGMEM  = "********";

// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;

Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, MQTT_PORT, MQTT_CLIENTID, MQTT_USERNAME, MQTT_PASSWORD);

/****************************** Feeds ***************************************/
const char TEMPERATURE_FEED[] PROGMEM = "sweethome/sensors/outdoor/temperature";
Adafruit_MQTT_Publish temperature_topic = Adafruit_MQTT_Publish(&mqtt, TEMPERATURE_FEED);

const char PRESSURE_FEED[] PROGMEM = "sweethome/sensors/outdoor/pressure";
Adafruit_MQTT_Publish pressure_topic = Adafruit_MQTT_Publish(&mqtt, PRESSURE_FEED);

const char LUMINOSITY_FEED[] PROGMEM = "sweethome/sensors/outdoor/lux";
Adafruit_MQTT_Publish luminosity_topic = Adafruit_MQTT_Publish(&mqtt, LUMINOSITY_FEED);

Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085);
Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_FLOAT, 12345);

/*************************** Sketch Code ************************************/

void setup() {
  Serial.begin(115200);
  delay(10);

  Serial.println("Sensor Test");
  if (!bmp.begin())
  {
    Serial.print("Ooops, no BMP180 detected ... Check your wiring or I2C ADDR!");
    while (1);
  }
  else {
    Serial.println("BMP180 ready.");
  }
  if (!tsl.begin())
  {
    Serial.print("Ooops, no TSL2561 detected ... Check your wiring or I2C ADDR!");
    while (1);
  }
  else {
    Serial.println("TSL2561 ready.");
  }

  configureTSL2561();

  // Connect to WiFi access point.
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);

  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("WiFi connected");
  Serial.println("IP address: "); Serial.println(WiFi.localIP());
}

void loop() {
  MQTT_connect();

  /* Get a new sensor event */
  sensors_event_t bmpEvent;
  bmp.getEvent(&bmpEvent);

  if (bmpEvent.pressure)
  {
    float temperature;
    bmp.getTemperature(&temperature);
    Serial.print("Temperature: ");
    Serial.print(temperature);
    Serial.println(" C");
    Serial.print("Publish Temperature: ");
    if (! temperature_topic.publish(temperature)) {
      Serial.println("Failed");
    } else {
      Serial.println("OK!");
    }

    float pressure = bmpEvent.pressure;
    Serial.print("Pressure:    ");
    Serial.print(pressure);
    Serial.println(" hPa");
    Serial.print("Publish Pressure: ");
    if (! pressure_topic.publish(pressure)) {
      Serial.println("Failed");
    } else {
      Serial.println("OK!");
    }
  }
  else
  {
    Serial.println("Sensor error");
  }

  sensors_event_t tslEvent;
  tsl.getEvent(&tslEvent);
  unsigned int luminosity = 0;
  if (tslEvent.light)
  {
    luminosity = tslEvent.light;
  }

  Serial.print("Luminosity:   ");
  Serial.print(luminosity);
  Serial.println(" lux");
  Serial.print("Publish Luminosity: ");
  if (! luminosity_topic.publish(luminosity)) {
    Serial.println("Failed");
  } else {
    Serial.println("OK!");
  }

  delay(5000);
}

void configureTSL2561()
{
  /* You can also manually set the gain or enable auto-gain support */
  // tsl.setGain(TSL2561_GAIN_1X);      /* No gain ... use in bright light to avoid sensor saturation */
  // tsl.setGain(TSL2561_GAIN_16X);     /* 16x gain ... use in low light to boost sensitivity */
  tsl.enableAutoRange(true);            /* Auto-gain ... switches automatically between 1x and 16x */

  /* Changing the integration time gives you better sensor resolution (402ms = 16-bit data) */
  tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_13MS);      /* fast but low resolution */
  // tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS);  /* medium resolution and speed   */
  // tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS);  /* 16-bit data but slowest conversions */
}

void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
    switch (ret) {
      case 1: Serial.println("Wrong protocol"); break;
      case 2: Serial.println("ID rejected"); break;
      case 3: Serial.println("Server unavailable"); break;
      case 4: Serial.println("Bad user/password"); break;
      case 5: Serial.println("Not authenticated"); break;
      case 6: Serial.println("Failed to subscribe"); break;
      default: Serial.print("Couldn't connect to server, code: ");
        Serial.println(ret);
        break;
    }
    Serial.println("Retrying MQTT connection in 5 seconds...");
    mqtt.disconnect();
    delay(5000);  // wait 5 seconds
  }
  Serial.println("MQTT Connected!");
}

To get the program running on the esp board you have to

Get the esp8266 into flash mode

press + hold “Reset” button
then
press + hold “GPIO0” button
then
release “Reset” button
then
release “GPIO0” button
-> the red LED turns on (with lower brightness)

flash

Ready for uploading

Open the Serial Monitor to see what’s going on:

arduino_ide_open_serial_monitor

Note the “Board”, “CPU Frequency”, “Upload Speed” and the “Port” settings! 

Before uploading you can build the program by click on “Verify”:

click_upload output_verify

Then start uploading:

click_upload output_uploadThe program starts immediately and the Serial Monitor shows whats going on:

serial_monitor

To debug/monitor the MQTT messages I am using MQTT.fx (of course ;-)):

mqttfx

No changes have to be made to other components of my networks. The replacement was transparent to the (Mobile) Clients also:

iphone

NetBeans 8 – Shortcut to Remote Platforms

The recently released NetBeans 8 holds a great gift for all Java Embedded Developers:

Build-in support for Remote Java Standard Edition platform.

To introduce this great and handy feature I created some screencasts:

Part1: headless

Part2: JavaFX on Pi

Part 3 – Remote Platform Debugging

José Pereda has also blogged about this feature in action: He and Mark Heckler used it in a distributed environment for their talk G-Force! Self-Powering Global Sensor Nets for IoT (JFokus 2014).

In addition to the support for a full JRE Environment its also worth to mention there is a nice editor as a fronted to the “recreate”-command provided by the all new EJDK 8:

ejdk

Update/Hint
To launch Raspberry PI Java applications from within Eclipse a project called LaunchPI has been started.

For Intellij/IDEA there is a plugin called “sourcesync” that might be helpful for even remote deployment.

But by now I can’t find other approaches that are such seamless and well integrated as NetBeans Remote Platform support.

Between 0 and 1 – PWM with Raspberry Pi

Switch on and off an LED and let run a motor at one speed and direction is easy, but what if I want to e.g. control the brightness of an LED or control motor-speed?

A kind of analog signal is needed:
This is done with Pulse-width modulation (PWM). PWM is a “modulation technique that conforms the width of the pulse, formally the pulse duration, based on modulator signal information.” (Wikipedia).

In a nutshell:
The average value of voltage is depending on the frequency the load is switched on and off.

Basically there are two ways to create PWM:

Hardware PWM

  • very fast (max. possible frequency / period)
  • software independent, independent from program flow
  • clean signal

BUT:

  • not all pins might support HW PWM

Software PWM

  • each I/O pin can be used for SW PWM

BUT:

  • software controlled timing has to be implemented
  • implementation and timing have to respect CPU usage
  • not a real clean signal

In other words: HW PWM is clearly prefered!

But unfortunately there is only one HW PWM pin (#18) available on the Rasperry Pis GPIO header (also its shared with the Audio system. That means that you can’t use PWM or Servo output and play audio through the 3.5mm jack at the same time).

If a RasPi project needs more than 1 PWM it has to use soft-pwm.
However thank god all the hard work is almost done by using Gordon Henderson’s wiringPi-lib and its Java-API Pi4J.

Pi4J provides a class called “SoftPwm”. As it uses wiringPi functions, the GPIO subsytem has to be initialzed first.
Therefore the programm has to executed as “sudo”!

Here is a simple example:

public class WiringPiSoftPWMExample {
    
    public static void main(String[] args) throws InterruptedException {
        
        // initialize wiringPi library
        Gpio.wiringPiSetup();

        // create soft-pwm pins (min=0 ; max=100)
        SoftPwm.softPwmCreate(0, 0, 100);

        IntegerProperty value1 = new SimpleIntegerProperty();
        value1.addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
            SoftPwm.softPwmWrite(0, newValue.intValue());
        });

        // continuous loop
        while (true) {
            // fade LED to fully ON
            for (int i = 0; i <= 100; i++) {
                value1.set(i);
                Thread.sleep(25);
            }

            // fade LED to fully OFF
            for (int i = 100; i >= 0; i--) {
                value1.set(i);
                Thread.sleep(25);
            }
        }
    }
}

LED blink

Works!
What’s next?

IMG_3176

3 x optical coupler + RGB LED stripe triggered by JavaFX UI contolled SoftPwms:

JavaFX on Raspberry Pi: Some fun with stepper motor

Recently had some fun when controlling my stepper motor with my Raspberry Pi.

The motor: a 28BYJ-48 with ULN2003 driver unit:

DSC_5687

Cool: Pi4J has a ready to use GpioStepperMotorComponent.

Let’s assume this architecture:

StepperMotorControl2

Thus we start with the StepperMotorAdapter using this component.

First some helpfull defines:

private final int oneRevolution = 2038;
private final int quarterRevolution = oneRevolution / 4;
private final int halfRevolution = oneRevolution / 2;
private final int oneDegreeRevolution = oneRevolution / 360;

then we need to get the GpioController and provide 4 GPIO pins set to digital output mode

gpio = GpioFactory.getInstance();

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)};

and we need a byte array to define a step sequence e.g. a single motor step sequence (every coil is turned on once per step):

singleStepSequence = new byte[4];
    singleStepSequence[0] = (byte) 0b0001;
    singleStepSequence[1] = (byte) 0b0010;
    singleStepSequence[2] = (byte) 0b0100;
    singleStepSequence[3] = (byte) 0b1000;

Now we can create the motor:

motor = new GpioStepperMotorComponent(pins);
        motor.setStepSequence(currentStepSequence);
        motor.setStepsPerRevolution(oneRevolution);

and some methods to control the motor:

public void stop() {
    motor.stop();
}

public void forward() {
    motor.reverse();
}

public void backward() {
    motor.forward();
}

public void oneStepBackward() {
    motor.step(oneDegreeRevolution);
}

public void oneStepForward() {
    motor.step(-oneDegreeRevolution);
}

public void halfRevolutionBackward() {
    motor.step(halfRevolution);
}

public void halfRevolutionForward() {
    motor.step(-halfRevolution);
}

public void quarterRevolutionBackward() {
    motor.step(quarterRevolution);
}

public void quarterRevolutionForward() {
    motor.step(-quarterRevolution);
}

This adapter is used by the StepperMotorControl which implements handlers like:

adjustBackwardButton.setOnMousePressed(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent t) {
        stepperMotorAdapter.backward();
    }
});
adjustBackwardButton.setOnMouseReleased(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent t) {
        stepperMotorAdapter.stop();
    }
});
adjustForwardButton.setOnMousePressed(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent t) {
        stepperMotorAdapter.forward();
    }
});
adjustForwardButton.setOnMouseReleased(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent t) {
        stepperMotorAdapter.stop();
    }
});

and provides FXML usable methods like:

@FXML
public void backward() {
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            stepperMotorAdapter.backward();
        }
    });
}

@FXML
public void forward() {
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            stepperMotorAdapter.forward();
        }
    });
}

@FXML
public void stop() {
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            stepperMotorAdapter.stop();
        }
    });
}

Finally this is the JavaFX UI running on the RasPi:
StepperMotorControl

Get the complete code here:

https://bitbucket.org/Jerady/raspberry-cpio-control-fx

Icons on the buttons are provided by:
FontAwesomeFX

RasPi does the Home Automation (Part IV): Cut the ‘rope’

Hi all,

until now all UI-Actions to control my wireless gears had to run directly on the Pi which controls the remote as e.g. deviceControl.turnOn(device); is called.

But now I’d rather make a web service available for this purpose to be more independently.
Question is: Which server should I use?

  • Apache/PHP: too less Java 😉
  • GlassFish/Java EE: too fat for this purpose and hard to take for a RasPi

But wait a minute: wasn’t there a HttpServer class that comes with Java6?
Using HttpServer I wrote a very small Server to provide the desired service.

Now I can switch my devices by an URL like this:

http://RASPI-IP:PORT/CONTEXT?houseCode=HOUSE-CODE&group=GROUP&device=DEVICE&command=COMMAND

e.g. http://raspi.home:22222/homeserver?houseCode=a&group=1&device=2&command=1

SEND URL:
RasPi_HomeServer1

SERVER (RE-)ACTION:
RasPi_HomeServer2

Recently my Pi received a command from Madrid (thanks José for supporting me ;-)):

2013-07-18 08_53_13-Twitter _ Interactions

For a more convenient usage with e.g. an JavaFX-EventHandler I use a HttpCommand to act like a DeviceContol:

public class SendHttpCommand {

    public static void main(String[] args) {
        Settings settings = new Settings();
        HttpCommand httpCommand = new HttpCommand(settings.getProperty("sweethome.server.ip.local"));
        Device terrace = new Device("Terrace", "a", "1", "2");
        String result = httpCommand.turnOn(terrace);
        System.out.println(result);
    }
}

Now the DevicePane looks like this:

public class DevicePane extends AnchorPane {

    @FXML
    private Button offButton;
    @FXML
    private Button onButton;
    @FXML
    private Text deviceNameText;

    private Device device;
    
    private DeviceControl deviceControl;
    private HttpCommand httpCommand;
    
    public DevicePane(final Device device) {
        this.device = device;
        init();
    }

    private void init() {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/DevicePane.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);
        try {
            fxmlLoader.load();
        } catch (IOException ex) {
            Logger.getLogger(DevicePane.class.getName()).log(Level.SEVERE, null, ex);
        }

        Settings settings = new Settings();
        httpCommand = new HttpCommand(settings.getProperty("sweethome.server.ip.local"));
        
        deviceControl = new DeviceControl();
        
        deviceNameText.setText(device.getName());

        offButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                turnOff();
            }
        });

        onButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                turnOn();
            }
        });
    }

    private void turnOn() {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                deviceControl.turnOn(device);
                httpCommand.turnOn(device);
            }
        });

    }

    private void turnOff() {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                deviceControl.turnOff(device);
                httpCommand.turnOff(device);
            }
        });

    }
}

RasPi does the Home Automation (Part III): ‘Let’s Put It All Together’

Hi,
now it’s time to put it all together:
1st: a mini Custom Control for each gear, looking like this:

Gear

Its controller takes a Device to trigger DeviceControl to switch the radio controlled gears:

public class DevicePane extends AnchorPane {

    @FXML
    private Button offButton;
    @FXML
    private Button onButton;
    @FXML
    private Text deviceNameText;


    public DevicePane(Device device) {
        init(device);
    }

    private void init(final Device device) {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/DevicePane.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);
        try {
            fxmlLoader.load();
        } catch (IOException ex) {
            Logger.getLogger(DevicePane.class.getName()).log(Level.SEVERE, null, ex);
        }

        deviceNameText.setText(device.getName());

        offButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                DeviceControl.get().turnOff(device);
            }
        });

        onButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                DeviceControl.get().turnOn(device);
            }
        });
    }
}

Finally the main controller HomeControlBoardController creates DevicePanes based on the configuration:

public class HomeControlBoardController {

    @FXML
    private Button exitButton;
    @FXML
    private AnchorPane root;
    @FXML
    private VBox devicesPane;

    public HomeControlBoardController() {
    }

    @FXML
    void initialize() {
        assert devicesPane != null : "fx:id=\"devicesPane\" was not injected: check your FXML file 'HomeControlBoard.fxml'.";
        assert exitButton != null : "fx:id=\"exitButton\" was not injected: check your FXML file 'HomeControlBoard.fxml'.";
        assert root != null : "fx:id=\"root\" was not injected: check your FXML file 'HomeControlBoard.fxml'.";

        // Running on RasPi always provide a way out instead of kill command
        final EventHandler<KeyEvent> exitOnCrtlCEventHandler =
                new EventHandler<KeyEvent>() {
            @Override
            public void handle(final KeyEvent keyEvent) {
                if (keyEvent.isControlDown() && KeyCode.C.equals(keyEvent.getCode())) {
                    Platform.exit();
                    keyEvent.consume();
                }
            }
        };
        root.setOnKeyPressed(exitOnCrtlCEventHandler);
        
        List<Device> devices = DeviceLoader.load();

        for (Device device : devices) {
            addDevice(device);
        }
    }

    private void addDevice(Device device) {
        DevicePane devicePane = new DevicePane(device);
        devicesPane.getChildren().add(devicePane);
    }

    @FXML
    public void exit() {
        Platform.exit();
    }
}

So finally (by now) it looks like this:

HomeControl2

Next: RasPi does the Home Automation (Part IV): Cut the ‘rope’

RasPi does the Home Automation (Part II): Crossing the bridge to JavaFX

Hi,

before JavaFX can take over, I need some bottom-up stuff to control my Intertechno-Devices.

Basically this is the schema:
RaspiSendSetup

Right now I have 2 radio intermediate adapters to switch the lights of my terrace/garden and the fountain. And one of these to control the lights in front of my house (cool: can be combined with existing light switch) :
ITL-230_assembly

Some Details:

Pragmatic approach: “Runtime.getRuntime().exec(...)
Clearly to pipe the data to the C implementation via a JNI call would be nice, but to get ahead in time by now for me it’s ok to use Runtime.getRuntime().exec(...) to run my “send” CLI command:

 public class Send {

  public enum Command {
    TURN_OFF, TURN_ON;
  }

  public static final String SEND_COMMAND = "/home/pi/rcswitch-pi/send";
  private static final Logger logger = Logger.getLogger(Send.class.getName());

  public void send(String deviveCode, Command command) {
    try {
      String commandLine = String.
              format("%s %s %s", SEND_COMMAND, deviveCode, command.ordinal());
      logger.log(Level.INFO, "send: {0}", commandLine);

      Runtime rt = Runtime.getRuntime();
      Process pr = rt.exec(commandLine);
      BufferedReader rd = new BufferedReader(new InputStreamReader(pr.
              getInputStream()));
      String line;
      while ((line = rd.readLine()) != null) {
        logger.log(Level.INFO, line);
      }

    } catch (IOException ex) {
      Logger.getLogger(Send.class.getName()).
              log(Level.SEVERE, null, ex);
    }
  }
  
  public boolean isSendCommandExecutable(){
      return Files.isExecutable(Paths.get(SEND_COMMAND));
  }
}

The Java-Send is used by DeviceControl:

public class DeviceControl {

    private Send send = new Send();
    private static final Logger logger = Logger.getLogger(DeviceLoader.class.getName());
    private static DeviceControl me;

    private DeviceControl() {
    }

    public static DeviceControl get() {
        if (me == null) {
            me = new DeviceControl();
        }
        return me;
    }

    public void turnOn(Device device) {
        logger.log(Level.INFO, "About to {0} {1} ({2})", new Object[]{Send.Command.TURN_ON, device.getName(), device.getId()});
        if (send.isSendCommandExecutable()) {
            send.send(device.getId(), Send.Command.TURN_ON);
        } else {
            logger.log(Level.SEVERE, "{0} could not be executed!", Send.SEND_COMMAND);
        }
    }

    public void turnOff(Device device) {
        logger.log(Level.INFO, "About to {0} {1} ({2})", new Object[]{Send.Command.TURN_OFF, device.getName(), device.getId()});
        if (send.isSendCommandExecutable()) {
            send.send(device.getId(), Send.Command.TURN_OFF);
        } else {
            logger.log(Level.SEVERE, "{0} could not be executed!", Send.SEND_COMMAND);
        }
    }
}

A Intertechno-appliance is represented by Device:

public class Device {

  private String name;
  private String houseCode;
  private String group;
  private String device;

  public Device() {
  }

  public Device(String name, String houseCode, String group, String device) {
    this.name = name;
    this.houseCode = houseCode;
    this.group = group;
    this.device = device;
  }

  public String getId() {
    return String.format("%s %s %s", houseCode, group, device);
  }

 [.... getter & setter ....]

}

The available Devices are defined via a configuration-file:

<?xml version="1.0" encoding="UTF-8"?>
<home-configuration>
    <devices>
        <device name="Haustür" houseCode="a" group="1" device="1"/>
        <device name="Terrasse" houseCode="a" group="1" device="2"/>
        <device name="Springbrunnen" houseCode="a" group="1" device="3"/>
    </devices>
</home-configuration>

Finally utility class DeviceLoader.load(); loads the configuration and provides a List of Devices.
The HomeControlBoardController dynamically creates DevicePanes according to the configured devices.

That’s it for now.

Next:
RasPi does the Home Automation (Part III): ‘Let’s Put It All Together’

RasPi does the Home Automation (Part I): Setting up the Pi

Hi all,

it’s some time ago since my last post as I was very busy due to other projects.
But I still intent to control my wireless power outlets with the Raspberry.

The hardware

In the meantime the receiver/transmitter components arrived from Hong Kong (took some weeks, but 8€ (incl. shipping!) for 5 pairs of receiver/transmitter is quite unbeatable ;-)):

DSC_5210

Next: connect the transmitter to PIN 0, +5V and GND:

DSC_5214

The software

To “talk” to the radio-controlled sockets you have to send serial data to a generic GPIO pin to be send by the transmitter, keyword: “bit-banging”.

Sadly by now Pi4J does not include any bit-banging techniques or built in support. Anyway bit-banging in Java may not be the best solution since timing cannot be guaranteed. (Thanks to Robert Savage from Pi4J for support)

OK then, down-to-earth: install required stuff on the Rasperry

  1. GPIO access library: wiringpi
  2. and the RaspPi adaption of RCSwitch (Arduino): rcswitch-pi

Then I replaced send.cpp from rcswitch-pi by my own version:


#include "RCSwitch.h"
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
  int PIN = 0;

  if (wiringPiSetup() == -1) return 1;
  RCSwitch mySwitch = RCSwitch();
  mySwitch.enableTransmit(PIN);

  if (argc==5)
  {
    char* houseCode = argv[1];
    int group = atoi(argv[2]);
    int device = atoi(argv[3]);
    int command  = atoi(argv[4]);
    printf("sending: houseCode:%c group:%i device:%i command: %i\n", houseCode[0], group, device, command);
    switch(command) {
    case 1:
        mySwitch.switchOn(houseCode[0], group, device);
        break;
    case 0:
        mySwitch.switchOff(houseCode[0], group, device);
        break;
    default:
        printf("command[%i] is unsupported\n", command);
        return -1;
    }
  }
  else
  {
    printf(" Usage: ./send <houseCode> <groupCode> <deviceCode> <command>\n");
    printf(" Command is 0 for OFF and 1 for ON\n");
  }

  return 0;
}

For convenience, I allow send to run as root:

sudo chown root ./send
sudo chmod 4711 ./send

My Intertechno devices can now be controlled from the prompt with my “send”-command:

./send [houseCode] [group] [device] [command]

Here we go:

pi@raspberrypi ~/rcswitch-pi $ ./send a 1 3 1
sending: houseCode:a group:1 device:3 command: 1
pi@raspberrypi ~/rcswitch-pi $

And the fountain in the garden ripples…

Next: “I need a UI” or Crossing the bridge to JavaFX

JavaFX on Raspberry Pi: GPIO Controller (Part2)

Part1 of this little series of articles was about the hardware setup of my little Raspberry Pi GPIO project.
Now let’s focus the software.

The Schema

RaspiGpioControllSchema

GpioAdapterFX

For the bridge between Java and the Raspberry’s GPIO I took advantage of the great work of the Pi4J Project guys.
The com.pi4j.io.gpio.GpioController is a straighforward and simple wrapper to manage the Pi4J GPIOController with JavaFX Properties:


public class GpioAdapterFX{

  private GpioController GPIO;
  private ObservableMap<BooleanProperty, ChangeListener<Boolean>> pins;
  private BooleanProperty pin0StateProperty;
  private BooleanProperty pin1StateProperty;
  private BooleanProperty pin2StateProperty;
  private BooleanProperty pin3StateProperty;
  private BooleanProperty pin4StateProperty;
  private BooleanProperty pin5StateProperty;
  private BooleanProperty pin6StateProperty;
  private BooleanProperty pin7StateProperty;
  private Timeline testTimeline;

  public CpioFXAdapter() {
    init();
  }

 private void init() {
    pin0StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin1StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin2StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin3StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin4StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin5StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin6StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin7StateProperty = new SimpleBooleanProperty(Boolean.FALSE);

    pins = FXCollections.observableHashMap();
    pins.put(pin0StateProperty, null);
    pins.put(pin1StateProperty, null);
    pins.put(pin2StateProperty, null);
    pins.put(pin3StateProperty, null);
    pins.put(pin4StateProperty, null);
    pins.put(pin5StateProperty, null);
    pins.put(pin6StateProperty, null);
    pins.put(pin7StateProperty, null);
  }

 private ChangeListener<Boolean> createPinListener(final GpioPinDigitalOutput pin) {
    return new ChangeListener<Boolean>() {
      @Override
      public void changed(ObservableValue<? extends Boolean> ov, Boolean oldValue, Boolean newValue) {
        if (newValue) {
          pin.high();
        } else {
          pin.low();
        }
      }
    };
  }

  /*
   * -------------------------- ACTIONS -------------------------- 
   */  
  public void connect() {

    GPIO = GpioFactory.getInstance();

     GpioPinDigitalOutput pin0 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_00, "PIN0", PinState.LOW);
    GpioPinDigitalOutput pin1 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_01, "PIN1", PinState.LOW);
    GpioPinDigitalOutput pin2 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_02, "PIN2", PinState.LOW);
    GpioPinDigitalOutput pin3 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_03, "PIN3", PinState.LOW);
    GpioPinDigitalOutput pin4 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_04, "PIN4", PinState.LOW);
    GpioPinDigitalOutput pin5 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_05, "PIN5", PinState.LOW);
    GpioPinDigitalOutput pin6 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_06, "PIN6", PinState.LOW);
    GpioPinDigitalOutput pin7 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_07, "PIN7", PinState.LOW);


    pins.put(pin0StateProperty, createPinListener(pin0));
    pins.put(pin1StateProperty, createPinListener(pin1));
    pins.put(pin2StateProperty, createPinListener(pin2));
    pins.put(pin3StateProperty, createPinListener(pin3));
    pins.put(pin4StateProperty, createPinListener(pin4));
    pins.put(pin5StateProperty, createPinListener(pin5));
    pins.put(pin6StateProperty, createPinListener(pin6));
    pins.put(pin7StateProperty, createPinListener(pin7));

    pin0StateProperty.addListener(pins.get(pin0StateProperty));
    pin1StateProperty.addListener(pins.get(pin1StateProperty));
    pin2StateProperty.addListener(pins.get(pin2StateProperty));
    pin3StateProperty.addListener(pins.get(pin3StateProperty));
    pin4StateProperty.addListener(pins.get(pin4StateProperty));
    pin5StateProperty.addListener(pins.get(pin5StateProperty));
    pin6StateProperty.addListener(pins.get(pin6StateProperty));
    pin7StateProperty.addListener(pins.get(pin7StateProperty));

    reset();
  }

 public void disconnect() {
    if (GPIO != null) {
      GPIO.shutdown();
    }
    for (BooleanProperty booleanProperty : pins.keySet()) {
      ChangeListener<Boolean> listener = pins.get(booleanProperty);
      if (listener != null) {
        booleanProperty.removeListener(listener);
      }
    }
  }

  public void setOnAllPins() {
    for (BooleanProperty booleanProperty : pins.keySet()) {
      booleanProperty.setValue(Boolean.TRUE);
    }
  }

  public void setOffAllPins() {
    for (BooleanProperty booleanProperty : pins.keySet()) {
      booleanProperty.setValue(Boolean.FALSE);
    }
  }

  public void reset() {
    if (testTimeline != null) {
      testTimeline.stop();
    }
    for (BooleanProperty booleanProperty : pins.keySet()) {
      booleanProperty.setValue(Boolean.FALSE);
    }
  }
 
}

SwitchBoardController

Finally the SwitchBoardController connects the UI to the GpioAdapterFX (bidirectional binding is my best friend).

To visualize the state of the GPIO Pins, I’m using Gerrit Grunwalds LED Control from his Enzo7 lib (a port of some of his JFXtras gauges to JavaFX 8).

public class SwitchBoardController implements Initializable {

  @FXML
  private GridPane buttonGridPane;
  @FXML
  private ToggleButton toogleGPIO0;
  @FXML
  private ToggleButton toogleGPIO1;
  @FXML
  private ToggleButton toogleGPIO2;
  @FXML
  private ToggleButton toogleGPIO3;
  @FXML
  private ToggleButton toogleGPIO4;
  @FXML
  private ToggleButton toogleGPIO5;
  @FXML
  private ToggleButton toogleGPIO6;
  @FXML
  private ToggleButton toogleGPIO7;
  @FXML
  private Button testButton;
  @FXML
  private Button resetButton;
  @FXML
  private Button exitButton;
  @FXML
  private ToggleButton gpioConnectButton;
  @FXML
  private ToggleGroup gpioConnectGroup;
  @FXML
  private ToggleButton gpioDisconnectButton;
  private GpioAdapterFX gpioAdapterFx;
  private Led indicatorGPIO0;
  private Led indicatorGPIO1;
  private Led indicatorGPIO2;
  private Led indicatorGPIO3;
  private Led indicatorGPIO4;
  private Led indicatorGPIO5;
  private Led indicatorGPIO6;
  private Led indicatorGPIO7;

  /**
   * Initializes the controller class.
   */
  @Override
  public void initialize(URL url, ResourceBundle rb) {
    assert buttonGridPane != null : "fx:id=\"buttonGridPane\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert exitButton != null : "fx:id=\"exitButton\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert gpioConnectButton != null : "fx:id=\"gpioConnectButton\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert gpioConnectGroup != null : "fx:id=\"gpioConnectGroup\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert gpioDisconnectButton != null : "fx:id=\"gpioDisconnectButton\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert resetButton != null : "fx:id=\"resetButton\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert testButton != null : "fx:id=\"testButton\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO0 != null : "fx:id=\"toogleCPIO0\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO1 != null : "fx:id=\"toogleCPIO1\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO2 != null : "fx:id=\"toogleCPIO2\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO3 != null : "fx:id=\"toogleCPIO3\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO4 != null : "fx:id=\"toogleCPIO4\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO5 != null : "fx:id=\"toogleCPIO5\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO6 != null : "fx:id=\"toogleCPIO6\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO7 != null : "fx:id=\"toogleCPIO7\" was not injected: check your FXML file 'SwitchBoard.fxml'.";

    gpioAdapterFx = new GpioAdapterFX();

    indicatorGPIO0 = createIndicator();
    indicatorGPIO1 = createIndicator();
    indicatorGPIO2 = createIndicator();
    indicatorGPIO3 = createIndicator();
    indicatorGPIO4 = createIndicator();
    indicatorGPIO5 = createIndicator();
    indicatorGPIO6 = createIndicator();
    indicatorGPIO7 = createIndicator();

    buttonGridPane
            .add(indicatorGPIO0, 0, 0);
    buttonGridPane.add(indicatorGPIO1, 1, 0);
    buttonGridPane.add(indicatorGPIO2, 2, 0);
    buttonGridPane.add(indicatorGPIO3, 3, 0);
    buttonGridPane.add(indicatorGPIO4, 4, 0);
    buttonGridPane.add(indicatorGPIO5, 5, 0);
    buttonGridPane.add(indicatorGPIO6, 6, 0);
    buttonGridPane.add(indicatorGPIO7, 7, 0);

    indicatorGPIO0.onProperty().
            bindBidirectional(gpioAdapterFx.pin0StateProperty());
    indicatorGPIO1.onProperty().
            bindBidirectional(gpioAdapterFx.pin1StateProperty());
    indicatorGPIO2.onProperty().
            bindBidirectional(gpioAdapterFx.pin2StateProperty());
    indicatorGPIO3.onProperty().
            bindBidirectional(gpioAdapterFx.pin3StateProperty());
    indicatorGPIO4.onProperty().
            bindBidirectional(gpioAdapterFx.pin4StateProperty());
    indicatorGPIO5.onProperty().
            bindBidirectional(gpioAdapterFx.pin5StateProperty());
    indicatorGPIO6.onProperty().
            bindBidirectional(gpioAdapterFx.pin6StateProperty());
    indicatorGPIO7.onProperty().
            bindBidirectional(gpioAdapterFx.pin7StateProperty());

    toogleGPIO0.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin0StateProperty());
    toogleGPIO1.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin1StateProperty());
    toogleGPIO2.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin2StateProperty());
    toogleGPIO3.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin3StateProperty());
    toogleGPIO4.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin4StateProperty());
    toogleGPIO5.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin5StateProperty());
    toogleGPIO6.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin6StateProperty());
    toogleGPIO7.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin7StateProperty());
  }

  private Led createIndicator() {
    return LedBuilder.create().
            prefHeight(80.0).
            prefWidth(80.0).
            build();
  }

  /*
   * -------------------------- ACTIONS -------------------------- 
   */
  @FXML
  public void testAction() {
    gpioAdapterFx.test();
  }

  @FXML
  public void resetAction() {
    gpioAdapterFx.reset();
  }

  @FXML
  public void gpioDisconnectAction() {
    gpioAdapterFx.disconnect();
  }

  @FXML
  public void gpioConnectAction() {
    gpioAdapterFx.connect();
  }

  @FXML
  public void exitAction() {
    gpioAdapterFx.reset();
    gpioAdapterFx.disconnect();
    Platform.exit();
  }
}

You can find the complete code here.

The runfxapp skript may help you to run the app on your Raspberry.

Stay tuned…

Inspired by Oliver Schneiders blog post I have already ordered the transmitter to control my wireless power outlets (but shipping from Hong Kong will take some weeks…).

JavaFX on Raspberry Pi: GPIO Controller (Part1)

I have started a new leisure time project with my Raspberry Pi.
Target: control the outside lighting around my house (yes, magic words these days: HOME AUTOMATION).

But let’s see how far I will get ;-).

Until now I created a very simple JavaFX UI to control and test the 8 GPIO-out pin states:

GPIO Control UI

But first lets have a look at the setup:

Used Hardware

System setup

Construction

So I armed myself with a soldering gun, brazed the Adafruit Pi Cobbler and sticked together the electical parts on the bread board according to the pin numbering:

WiringPi pin number scheme

gpio-control-example
(Source: pi4j.com)

DSC_3980

DSC_3982

And: Action!

Part2