Talking to a ZWave Switch using open-zwave

Z-Wave is a wireless communications protocol designed for home automation, specifically to remotely control applications in residential and light commercial environments. The technology uses a low-power RF radio embedded or retrofitted into electronics devices and systems, such as lighting, home access control, entertainment systems and household appliances.

The are many devices that have these RF chips embedded inside them. In this post you can see an example on how you can program Z-Wave switches using some OpenSource tools. We will use the open-zwave library. You can use it to interface with all available Z-Wave PC controllers. We have the Aeon Labs Z-Stick S2 and a Raspberry Pi.

The open-zwave library is great because it is OpenSource, you can easily build it for Raspberry and use the Raspberry as a hub for your home automation system. In this way with one Raspberry you can remotely control all your Z-Wave enabled devices at home – things like light switches, open/closed sensors, power meters, etc.

The first step is to download and build the open-zwave library on your Raspberry. Great Tutorial for more details. Here is a recap of what you need to do:

sudo apt-get install subversion libudev-dev make build-essential git-core svn checkout http://open-zwave.googlecode.com/svn/trunk/ open-zwave-read-only ~/open-zwave-read-only/cpp/build/linux $ make

Then you’re ready to build the MinOZW example:

~/open-zwave-read-only/cpp/examples/linux/MinOZW $ make

Check if everything is alright by running the test:

./test

Make sure that the Z-Wave controller is connected to the USB drive of the Raspberry. If everything goes alright the MinOZW example will scan for new Hadrware and generate some debug files that list all discovered devices. Note that the controller can discover a device only if they have previously been paired together. Pairing devices together means that they would belong to the same network.

Now, we’re going to write our own test application! We’re going to first pair the controller with a Z-Wave Switch. Then in the test application we will turn the switch on and off from the code running on the Raspberry Pi. First after we have paired the devices we have to run the MinOZW executable like we did above and explore the generated zwcfg_0xYOUR_HOMEID.xml config file. It contains detailed information about all the sensors and controllers in the local ZWave network

Digging into the code of the examples/linux/MinOZW/Main.cpp file we can see that it uses the Manager class as the main public interface to OpenZWave.

Talking to a ZWave Switch using open-zwaveFirst you need to say something like:

Manager::Create();
Manager::Get()->AddWatcher( OnNotification, NULL );
Manager::Get()->AddDriver( this->port );

Where port is the device address of your Z-Wave controller – for example: /dev/ttyUSB0

All Z-Wave functionality is accessed via the Manager class. It is a singleton class that exposes all the functionality required to add Z-Wave support to an application. It handles the sending and receiving of Z-Wave messages as well as the configuration of a Z-Wave network and its devices, freeing the library user from the burden of learning the low-level details of the Z-Wave protocol.

There can be only one instance of the Manager class, and all applications will start by calling Manager::Create static method to create that instance. From then on, a call to the Manager::Get static method will return the pointer to the Manager object. On application exit, Manager::Destroy should be called to allow OpenZWave to clean up and delete any other objects it has created.

Manager::Get()->AddWatcher( OnNotification, NULL );

Once the Manager has been created, a call should be made to Manager::AddWatcher to install a notification callback handler. This handler will receive notifications of Z-Wave network changes and updates to device values, and is an essential element of OpenZWave.

Manager::Get()->AddDriver( this->port );

Next, a call should be made to Manager::AddDriver for each Z-Wave controller attached to the PC. Each Driver will handle the sending and receiving of messages for all the devices in its controller's Z-Wave network.

Finally we want to set the value of the Z-Wave Switch. We will do this with the following code:

    
void SetValue(bool value)
{
    int nodeid = 3;
    pthread_mutex_lock( &g_criticalSection );
    for( list<NodeInfo*>::iterator it = g_nodes.begin(); it != g_nodes.end(); ++it )
    {
                NodeInfo nodeInfo = it;
                if( nodeInfo->m_nodeId != nodeid ) continue;
        for( list<ValueID>::iterator it2 = nodeInfo->m_values.begin();
 it2 != nodeInfo->m_values.end(); ++it2 )
        {
            ValueID v = *it2;
            if( v.GetCommandClassId() == 0x25 )
            {
                Manager::Get()->SetValue(v, value);                
                break;
            }
        }
    }

    pthread_mutex_unlock( &g_criticalSection );
}

You can see the Command Classes Ids here 0x25 is the Id of the COMMAND_CLASS_SWITCH_BINARY command. Therefore in the if statement we check that we set the value using the Manager class only when GetCommandClassId() is 0x25.
Note the if statement:

if( nodeInfo->m_nodeId != nodeid) continue;

You need to determine the right node id number of the Z-Wave Switch in your Z-Wave network. To do this you can have a look at the zwgcfg file. It would say something like:

<Node id="3" type="Binary Power Switch">
    <Manufacturer id="0086" name="Aeon Labs">
        <Product type="0003" id="0012" name="Unknown: type=0003, id=0012" />
    </Manufacturer>
    <CommandClasses>
        <CommandClass id="32" name="COMMAND_CLASS_BASIC" version="1" request_flags="4" mapping="37">
            <Instance index="1" />
        </CommandClass>
        <CommandClass id="37" name="COMMAND_CLASS_SWITCH_BINARY" version="1" request_flags="4">
            <Instance index="1" />
            <Value type="bool" label="Switch" units="" value="True" />
        </CommandClass>
    </CommandClasses>
</Node>

In the next post you will see how we used all of this to create a Z-Wave Switch Driver, build it on the Raspberry Pi and load it into Agilart.

0 Comments

 

Designing ‘sensing' Scenarios

In the previous post you can find a bunch of code and explanations about how to use a Temperature Sensor like the TMP102 with a Raspberry Pi. But what if you can skip that part and go right into prototyping what you really wanted to do with your sensor. This is what we aim to do in Agilart – where you can visually drag and drop widgets that can represent real stuff like sensors and devices that you can attach to a Raspberry Pi.

Let’s say that you want to monitor the temperature inside your kids room at home and make sure that it is never cold or that I want to monitor the temperature of a stove that I’m producing. I can use the Raspberry Pi to run Agilart and then in the online Agilart Dashboard I can do all kinds of scenarios like these two.

In this specific case I want to use the Temperature Sensor widget which represents the actual TMP102 sensor device. Then for example I can connect it to an Online Feed. I can see these feeds on the website and get notifications when the data coming in the feed meets specific criteria(for example when it’s too cold or too hot).
In Agilart this will look like the diagram on the right using two widgets – the Temperature Sensor and the Online Feed widgets.

How does it work?

Agilart has a plug-in architecture that allows you to add new widgets dynamically. Some of them are software related – like the Comparator widget bewol(>=) and others are Hardware related – like the Temperature Sensor. Therefore for the hardware widgets you can plug in different drivers that can do whatever they want to when they are activated.

As a whole:

The widgets that are somehow connected to sensors and other hardware have drivers

A driver can execute commands.

You define the commands your widget can support.

For instance here is what we did with the Temperature Sensor Widget. You can see the code in Github: https://github.com/Agilart/Agilart-Run-Time
We have written a very simple Driver that would handle the low level communication between the Raspberry Pi and the TMP102 sensor through I2C. It can execute a few commands(that would be coming from the widget).

void TemperatureDriver::ExecuteCommand( Command command )
{
	EventLogger::Instance()->WriteVerbose("Temperature Driver - Execute Command %s", command.Name().c_str());

	if ( !strcmp( command.Name().c_str(), INIT_CMD ) )
	{
		CommandParameter* nameParam = command.GetParameter( TMP_DEVICENAME );
		CommandParameter* addressParam = command.GetParameter( TMP_DEVICEADDRESS );
		CommandParameter* refreshParam = command.GetParameter( TMP_REFRESH );

		this->devName = "/dev/" + *((string*) ( nameParam->Value ));
		this->address = *((string*) ( addressParam->Value ));
		this->refreshOn = *((int*) ( refreshParam->Value ));
		this->shouldStop = false;

		if( this->OpenPort() )
			this->StartReadingThread();
	}
	else if( !strcmp( command.Name().c_str(), STOP_CMD ) )
	{
		this->StopReadingThread();
	}
}

This is the main method that you would want to modify if you create a new driver. It is defined in the base class – BaseDriver. Here you want to say what should happen when a command comes from the widget. In the Temperature Sensor we have only some basic commands – the INIT_CMD which is called when the scenario starts running and the STOP_CMD which is executed when the scenario is interrupted.

As you see here the commands can have parameters which you can access from the driver. In our case in order to set up the Temperature Sensor we need to know the specifics of the hardware – the device: “/dev/i2c-0” and the address: “0x48” (from the example in the previous post). We have one more parameter – refreshOn which says how often should the driver check its status.

On the other hand we have the Temperature Sensor widget/device that would appear in the online designer. It is a class that derives from BaseDevice and defines the commands and parameters it will use to communicate with its driver. Here is it's constuctor:

TemperatureDevice::TemperatureDevice() : BaseDevice()
{
	this->connectionPoints = new list<ConnectionPoint*> ();
	this->pinouts = new std::list<Pinout*>();

	this->outputConnPoint = new OutConnectionPoint( 1, "Output",
			"Temperature Sensor Device Output", FLOAT, NULL );

	this->outputConnPoint->RegisterRefreshHandler( this );
	this->connectionPoints->push_back( this->outputConnPoint );

	// Button Pionout definition:
	Command cmdInit( INIT_CMD );
	Command cmdStop( STOP_CMD );

	list<Command>* pinoutSupportedCommands = new list<Command>();
	pinoutSupportedCommands->push_back( cmdInit );
	pinoutSupportedCommands->push_back( cmdStop );

	list<Event>* pinoutSupportedEvents = new list<Event>();
	list<EventParameter>* eventParams = new list<EventParameter>();
	eventParams->push_back( EventParameter( TMP_VALUE, 0, "float" ) );

	Event eventClick( TMP_CHANGED, eventParams );
	pinoutSupportedEvents->push_back( eventClick );

	this->pinout = new Pinout( 0, "Temperature Sensor Pinout", "Sensor",
			pinoutSupportedCommands, pinoutSupportedEvents );
	pinout->PinoutEventTriggered = &PinoutEventTriggered;

	this->pinouts->push_back( pinout );
}

Here you can see that we have fined:

A list of connection points – these are the the inputs and outputs of our widget which would allow it to communicate with other widgets

A pinout – the pinout is the way to say that this widget will be related to real hardware and it should have a driver attached to it. You can have as many pinouts as you wish, which means that you can attach a different driver to each of the pinouts and in this way even reuse driver across different widgets. The pinouts also holds the definitians of the commands and events that will flow between the widget and its drivers.

Designing with your widgets

Having this infrastructure allows us to create all kinds of scenarios using the widgets. Here are some ideas where we can use the Temperature Sensor widget, like monitoring the temperature in the basement or in the garage or even incide the PC!

Measuring temperature is very easy when you have a Pi and a temperature sensor like the TMP102. What’s great about the TMP102 is that

  • it’s tiny
  • it doesn’t require a lot power (1.4V to 3.6VDC)
  • accurate (senses differences up to 0.5°C)
  • it communicates via the I2C bus

The Raspberry Pi let’s you easily communicate with the TMP102 using I2C. There are a lot of examples about how to test the sensor but actually there’s not much out there if you want to write a C/C++ executable that would use the I2C bus to communicate with the sensor. This post is exactly about playing around with a temperature sensor and the Raspberry Pi I2C bus from a very simple C executable.

Configuring the Raspberry Pi I2C port

The Raspberry Pi I2C port is not enabled by default. First You need to enable it by removing it from the blacklist with one of the Pi’s config files:

$sudo vi /etc/modprobe.d/raspi-blacklist.conf

Once this file is open find this line blacklist i2c-bcm2708 and comment it out by adding a # to the front of it.

#blacklist i2c-bcm2708

Save the file and reboot. To use the I2C bus from userspace you’ll need to load the i2c-dev module by running this command:

$sudo modprobe i2c-dev

Now if you check your /dev/ directory you should see the i2c devices listed among the others. On the Raspberry you should see /dev/i2c-0 and /dev/i2c-1 listed. To be able to use these ports you need to give yourself permission to access them.

$sudo chmod o+rw /dev/i2c*

Now you are ready to proceed. However once you reboot the Pi you will have to do the last two commands again. Therefore you can add them to the /etc/rc.local script. To learn more about the Raspberry Pi Kernel modules and how to write your own modules check Chris’s blog here

The Wiring

I2C is a 2-wire serial connection, SDA (Data) and SCL (Clock) – On your Raspberry Pi SDA is on GPIO 0, and SCL is on GPIO 1. Have a look at the schematics here. Here is a picture:

I2C allows you also to have connect more than 1 sensors to your Pi. Note that the TMP102 sensor has a ADD0 pin. If you connect this pin to Ground the address of the device on the Pi would be 0x48. If you connect it to VCC it would be 0x49. We would need these address later in the code. Here is how we can wire two temperature sensors on a breadboard.

The Code

Here is the code. Note that to communicate with through the I2C bus we are using the built in libraries. To do this we need to know the device we want to use and the address. Here the address is 0x48 and the wiring follows the first image above. The sensor has 4 registers that are accessible through the I2C bus:. Register #0 is the Temperature Register, it is Read Only and is the one we will be using. Other than this we have Register #1 which is a Read/Write register used for configuration. When we start reading from Register #0 we are interested in the first two bytes, which are:

  • Byte 1: Full degrees
  • Byte 2: Fraction degrees

To convert to °C, we use these two bytes, shift right by 4, convert to decimal and multiply by 0.0625. Read more about the Registers details at Donal's blog post here

#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    unsigned int temp = 0;
    unsigned char msb, lsb;

    printf("**** Tmp102 test program ****,\n");
    int fd;                                    // File descritor
    char *fileName = "/dev/i2c-0";             // Name of the port we will be using
    int  address = 0x48;                       // Address of TP102
    unsigned char buf[10];                     // Buffer for data being read/ written on the i2c bus

    if ((fd = open(fileName, O_RDWR)) < 0) {   // Open port for reading and writing
            printf("Failed to open i2c port\n");
            exit(1);
    }

    if (ioctl(fd, I2C_SLAVE, address) < 0) {  // Set the port options and set the address of the device we wish to speak to
            printf("Unable to get bus access to talk to slave\n");
            exit(1);
    }


    buf[0] = 0;                                // This is the register we wish to read from

    if ((write(fd, buf, 1)) != 1) {            // Send register to read from
            printf("Error writing to i2c slave\n");
            exit(1);
    }

    if (read(fd, buf, 1) != 1) {                // Read back data into buf[]
            printf("Unable to read from slave\n");
            exit(1);
    }
    else {
            msb = buf[0];
            printf("0x%02X ", msb);

            if (read(fd, buf, 1) != 1) {         // Read back data into buf[]
                    printf("Unable to read from slave\n");
                    exit(1);
            } else {
                    lsb = buf[0];
                    temp = (msb<<8) | lsb;
                    temp >>= 4;

                    printf("The temperature is: %f \n",temp*0.0625);
            }
    }

    return 0;
}

Talking to a ZWave Switch using open-zwave Schematic

Just a few days ago we got a great surprise from Olimex – the new Olinuxino A13! After setting up the SD card, installing Debian and trying the Agilart platform works on it we wanted to set up the developing environment in order to make it easy to cross compile and cross debug C/C++ code that would run on the microcontroller from the host machine.

So here is how we worked our way through this and managed to get cross compiling and cross debugging working with the standart GCC and GDB on the Olinuxino Micro board. The same set of steps would work for other microcontrollers too, you just need to have the right toolchain.

First make sure that you have installed the Eclipse CDT plugin. You cal also install the whole Eclipse IDE for C/C++ Developers. Then you need the appropriate arm toolchain against which you can cross compile your code. In order to generate programs that can run and be debugged on the Olinuxino, we need to install the appropriate compiler and debugger and set up the project in Eclipse.

Setting up your toolchain for cross compilation

You don't need ARM based host to develop software for Olinuxino. You can do everything with Ubuntu and a proper cross compiler. To produce code that will run on the Olinuxino you need a toolchain with ARMv5 support. Because Ubuntu has default toolchain arm-linux-gnueabi for ARMv7arm, solution is to get proper toolchain from Debian repositories.

sudo apt-get install gcc g++ make libncurses5-dev

Add the following line to /etc/apt/sources.list

deb http://www.emdebian.org/debian/ squeeze main

Install the following packages:

sudo apt-get install linux-libc-dev-armel-cross
sudo apt-get install libc6-armel-cross
sudo apt-get install libc6-dev-armel-cross
sudo apt-get install binutils-arm-linux-gnueabi
sudo apt-get install gcc-4.4-arm-linux-gnueabi
sudo apt-get install g++-4.4-arm-linux-gnueabi
sudo apt-get install uboot-mkimage

Set up a GCC Cross compiler project in Eclipse

Open Eclipse and click on File >> New >> C++ Project

In the Project Type section select the Cross-compile Project. This is how eclipse will know that we don't want to use the standart gcc but another tooolchain(arm-unknown-linux-gnueabi-gcc). When you create the project add a simple main.cpp source file. Here we'll do the ‘Hello World' in this case ‘Hello Olinuxino' project. Then go to the project's Settings page and check the cross compiler configuration settings.

 

For more detail: Talking to a ZWave Switch using open-zwave


About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top