Skip to content

Tutorial

petur03 edited this page Feb 21, 2012 · 5 revisions

Tutorial

1. Introduction

This tutorial is a step by step guide for the building of an adapter. This adapter will be the bridge that will provide access to your Devices/Services using a HomePort daemon. For this tutorial we will use a simple example of one Device connected to the adapter that contains 2 Lamps and 2 Switches.

Two examples are included in the project, they are available under src/examples. The main files for the examples are hpd_example.c and hpd_example_phidget.c.

2. Launching the HomePort daemon

The HomePort daemon will be the core for your adapter the first step is to launch it using the function provided by the library :

#include <hpdaemon/homeport.h>

int HPD_start( unsigned int option, char *hostname, ... );

This function takes several options arguments that are described in the documentation. For our example, we will use :

HPD_start(HPD_USE_CFG_FILE, "Homeport", HPD_OPTION_CFG_PATH, "./hpd.cfg");

Which means that our adapter will be called HomePort and will use a configuration file in order to initialize itself. We will thus need to build that configuration file.

3. Building the Configuration File

The configuration file has two distinct fields. One for the daemon and the other for the log. As we are not willing to use secure communication for the example, the hpdaemon field will only have one attribute : the port on which the server will be running. As for the log field, the three attributes will be needed : the log file name, the maximum size of the log and the level of the log (0 1 or 2). Our configuration file will then be :

hpdaemon = 
{ 
	http_port = 8888; 
} 

hpdlog = 
{ 
       log_file_name = "hpd_log.log"; 
       max_log_size = 2048; 
       log_level = 2; 
}

4. Creating the Devices and Services

Now that our daemon is up running, we will need to create the different Devices and their associated Services in our system. To do so we begin by creating the Devices using :

Device* create_device_struct( char *description, 
                             char *ID, 
                             char *vendorID, 
                             char *productID, 
                             char *version, 
                             char *IP, 
                             char *port, 
                             char *location, 
                             char *type, 
                             int secure_device);

It is very important to note that the fields ID and type must not be NULL, indeed, those two fields will determine the uniqueness of our Device in the system. For our example we will have :


Device *device = create_device_struct("This is an Example Device", 
				      "1", 
				      "0x01", 
				      "0x01", 
				      "V1", 
				      NULL, 
				      NULL, 
				      "LivingRoom", 
				      "ExampleType",					
				      HPD_NON_SECURE_DEVICE);

Notice that the last field secure_device determines if the access to the Device should be restricted or not (HPD_SECURE_DEVICE or HPD_NON_SECURE_DEVICE). Our Device contains Services that we now need to create using :

Service* create_service_struct(char *description, 
                                char *ID, 
                                char *type, 
                                char *unit, 
                                Device *device, 
                                HPD_GetFunction get_function, 
                                HPD_PutFunction put_function, 
                                Parameter *parameter, 
                                void* user_data_pointer);

Again the fields ID and type must not be NULL because they will determine the uniqueness of the Service. For our example we will have 4 Services : 2 Lamps and 2 Switches. For the creation of Services, as you can notice, we need a Device but also a HPD_GetFunction and a HPD_PutFunction. Those functions will be the core functions for the use of the Service. Indeed, the HPD_GetFunction (which is of course mandatory) will be the function that will retrieve the value of the Service that you are willing to create. The HPD_PutFunction will be the function that will be used to remotely modify the value of the service. Here are the interface of those functions :

size_t (*HPD_GetFunction) (	Service* service, 
				char *buffer, 
				size_t max_buffer_size );

size_t (*HPD_PutFunction) (	Service* service, 
				char *buffer, 
				size_t max_buffer_size, 
				char *put_value );

Both of these functions will use a buffer to interact with the daemon, indeed the buffer has to be stock in the char *buffer and the size of the buffer has to be returned. In case of errors, you need to return 0. For the HPD_PutFunction, the value that will be replacing the current value will be passed using char *put_value , it is your responsibility to convert it to the desired format. For our example we will have :

size_t get_lamp (	Service* _service, 
			char *buffer, 
			size_t max_buffer_size ) 
{ 
	int value = get_value_of_lamp_0();
	sprintf(buffer,"%d", value); 
	return strlen(buffer); 
}

size_t put_lamp (	Service* _service, 
			char *buffer, 
			size_t max_buffer_size, 
			char *put_value );
{ 
	int ack = modify_value_of_lamp_0(put_value);
	sprintf(buffer,"%d", ack); 
	return strlen(buffer); 
}

While designing these functions, you will also need to make sure that the size of the buffer does not exceed max_buffer_size. As soon as our HPD_GetFunction and HPD_PutFunction are ready, we can design the Parameter for our Service using :

Parameter* create_parameter_struct( char *ID, 
                                    char *max, 
                                    char *min, 
                                    char *scale, 
                                    char *step, 
                                    char *type, 
                                    char *unit, 
                                    char *values );

For our example :

Parameter* lamp0_param = create_parameter_struct("0", 
                        		         "1", 
                                   		 "0", 
                                   		 NULL, 
                                   		 "1", 
                                   		 NULL, 
                                   		 NULL, 
                                   		 "0,1");

Now we can create our Services :

Service *service_lamp0 = create_service_struct ("Lamp0", 
						"0", 
						"Lamp", 
						"ON/OFF", 
						device,
						get_lamp,
						put_lamp,
						lamp0_param,
						NULL);

The last step of the launching of those Services over the HomePort network is to register those services to the daemon using one of those two functions :

int HPD_register_service( Service *service_to_register ); int HPD_register_device_services( Device *device_to_register );

Meaning for our example :

HPD_register_device_services( device );

Our Services are created and registered, but also thanks to the daemon advertised using ZeroConf. It is your choice to choose how your system will wait, for the example, a simple getchar() is enough. Don't forget that, before the end of your adapter lifetime, you will need to unregister the Services and stop the Daemon using :

HPD_unregister_service (_service_lamp0); 

or

HPD_unregister_device_services(_secure_device); 

and

HPD_stop ();

5. Accessing the Services

After having launched your adapter, it is pretty simple to access your Services, indeed, they will all be advertised using ZeroConf. Those advertisements will give you the URL with which you can access your Services. For our example, if I want to know the value of my Lamp 0, I will perform a GET on the URL :

http://Adapter's IP:8888/ ExampleType/1/Lamp/0

The value will then be given to you with an XML of the form :

<?xml version="1.0" encoding="UTF-8"?>
<value timestamp="xxxxxxxx">returned_value</value>

If you want to change the value of a desired Service, it is also pretty simple, you will have to perform a PUT request on the Service URL with an XML stating the desired value. For example, performing a PUT on

http://Adapter's IP:8888/ ExampleType/1/Lamp/0

with :

<?xml version="1.0" encoding="UTF-8"?>
<value>1</value>

Will set the value of the Lamp0 to 1 depending on the implementation of the HPD_PutFunction stated before.

6. Server-Sent Events

Coming soon