Home > Blog > Thunderheads > Thunderheads: February 2012

Thunderheads: February 2012

Projects from the Mind of Mark

Mark Geller’s Arduino Project
Part Four

In my previous articles I showed you how to configure an Arduino project to communicate with a ConnectPort® X gateway, how to send serial commands via iDigi® web services, how to build a mobile web site that uses iDigi web services and how to build a native iOS application that uses iDigi web services. For this installment, we are going to look at how we can upgrade our existing Arduino sketch so that it reports which lights are on and how to write a custom iDigi® Dia driver in Python. If you haven’t had a chance to read the previous articles, please take a moment to check them out on the iDigi blog.

Parts List

Project Requirements

There were two shortcomings with the original project.

First, there wasn’t a way to see which LEDs on the Arduino project were on without looking at the hardware. It would be nice if we could send a command that would report back the state of the LEDs or if the current state of the LEDs was reported back whenever it changed.

Second, it would be nice if we could have a central place to track when the LEDs turn on or off.Here is a list of requirements for this update that will address these shortcomings.

  • Send the current status of all the LEDs if any of the LEDs turns on or off.
  • Create a simple command that will report the current state of all the LEDs.
  • Upload the serial data from the Arduino project to the iDigi Device Cloud.

From these requirements, it is clear that we need to update the Arduino sketch. And although the iDigi Dia XBee serial terminal driver already handles reading and writing serial data from XBee modules, we are going to want to create our own iDigi Dia device driver so we can precisely control how our project data is uploaded to the iDigi® Device Cloud™.

Updating the Arduino Sketch

Step 1

We are going to start by updating the switchCase2 example Arduino sketch. First we are going to add some variables to keep track of each LED’s state. Add the following code above the setup() method:

int firstLight = 0;
int secondLight = 0;
int thirdLight = 0;
int fourthLight = 0;
int fifthLight = 0;

Step 2

Now we need to update the switch statement in the loop() method so that the variables we added in step 1 are updated when the LEDs are turned off or on. We are also going to add a command that will report back the state of the LEDs without changing them. Here are the changes to the switch statement:

switch(inByte) {
  case 'a':
    digitalWrite(2, HIGH);
    firstLight = 1;
    break;
  case 'b':
    digitalWrite(3, HIGH);
    secondLight = 1;
    break;
  case 'c':
    digitalWrite(4, HIGH);
    thirdLight = 1;
    break;
  case 'd':
    digitalWrite(5, HIGH);
    fourthLight = 1;
    break;
  case 'e':
    digitalWrite(6, HIGH);
    fifthLight = 1;
    break;
  case 'r':
    break;
  default:
    // turn all the LEDs off;
    for (int thisPin = 2; thisPin < 7; thisPin++) {
      digitalWrite(thisPin, LOW);
    }
    firstLight = 0;
    secondLight = 0;
    thirdLight = 0;
    fourthLight = 0;
    fifthLight = 0;
}

Step 3

Now we need a way to report back the current state of each LED. All LEDs that are on should report a value of 1 while all the LEDs that are off should report a value of 0. We also want to add an end of line character so that any program reading this datacan see where each statement ends. To keep things simple, we are going to write ascii data to the serial port. Add this code below the switch statement:

Serial.print(firstLight);
Serial.print(secondLight);
Serial.print(thirdLight);
Serial.print(fourthLight);
Serial.print(fifthLight);
Serial.print(":");

Step 4: Testing

Now is a good time to test our Arduino sketch to make sure everything is working. Click the “Upload” button in the Arduino IDE to compile and upload your sketch to your Arduino board.

NOTE: Don’t forget to disconnect the transmit and receive wires from the XBee module on your Arduino board before you try to upload your sketch.Click the “Serial Monitor” button in the Arduino IDE to open up a serial terminal session. Enter the letter ‘a’ into the text box and click the send button. You should see 10000: printed in the terminal window and you should see the first LED turn on. Now we are going to verify that sending the letter ‘r’ will report back the state of all the LEDs without turning any on or off. Enter the letter ‘r’ into the text box, click the send button and verify that 10000: was printed in the terminal window.

Now we are going to try a more complicated command. Enter ‘fabc’ into the text box and click the send button. You should see 00000:10000:11000:11100: printed to the terminal window. As the Arduino sketch parses each letter in the serial command, it will write the current state to the serial port. We are going to want to account for this behavior in our custom iDigi Dia device driver and make sure we only report the final sequence to the iDigi Device Cloud.

We are done writing the Arduino sketch. Don’t forget to reconnect the transmit and receive wires from the XBee module to your Arduino board.

Creating an iDigi Dia Device Driver

We are going to use the iDigi Dia framework to handle sending data to the Arduino board. The iDigi Dia framework was built to make it easy to communicate and configure XBee devices and to take advantage of the iDigi Device Cloud. We are going to create a new device driver for our Arduino board based off the XBee serial terminal device driver.

Step 5: Create a new iDigi Dia project

Start the Digi ESP™ for Python development environment. If you don’t have Digi ESP installed, you can download it from the Digi web site. Select “File > New > iDigi Dia Project” from the menu to create a new project. Use the project wizard to create your project. Click the help icon in the lower left corner of the project wizard window to view the help documentation.

Step 6

Right click or command click on the “custom_devices” folder in your project and select “New > Python Module” to create a new module. I named my module “xbee_arduino”.

 

 

Step 7

Open the “xbee_serial_terminal.py” module. You can find it in the src/devices/xbee/xbee_devices/ package. Copy all the code from this file into your xbee_arduino file.

 

Step 8

We are going to store incoming serial data from the XBee module in a buffer before processing it. We are going to specify the maximum buffer size as a module constant. Add the following code just below the import and from statements at the top of the file:

 

MAX_RX_BUFFER = 512

 

 

Step 9

Change the class name from “XBeeSerialTerminal” to “XBeeArduino”.

Step 10

We need to create a local variable to hold our receive buffer. Add the following code in the __init__ method just below the self.__xbee_manager = None line:

        # the buffer that holds the serial data before parsing
        self.__rx_buffer = ""

 

 

Step 11

The XBee serial terminal driver has 2 data channels: read and write. We are going to change the name of the read channel to “lights” and give it a default value of “00000″. Change the first ChannelSourceDeviceProperty so that it matches the code below:

ChannelSourceDeviceProperty(name="lights", type=str,
    initial=Sample(timestamp=0, unit="", value="00000"),
    perms_mask=DPROP_PERM_GET, options=DPROP_OPT_AUTOTIMESTAMP),

 

 


Step 12

Our custom driver extends the base XBee serial device driver. The XBee serial device driver handles configuring and communicating with the XBee module, but it does require that you implement the following methods in your driver:

  • read_callback — This method is called when the XBee module receives a message.
  • apply_settings — This method is called during setup. It allows you to read and apply settings from your project yml file.
  • start — This method is called when the device driver starts. All the settings for the driver are available at this point so this is where you register your driver with the XBee device manager and run any initialization code, such as starting threads.
  • stop — This method is called when your device driver is stopped.

Luckily the code we copied from the XBee serial terminal driver implements all these methods for us. It even has basic implementations for these functions:

  • serial_read — This method is called when serial data is received.
  • serial_write — This method is called when a new value is set for the “write” channel on our driver. It tells the gateway to transmit the data in the “write” channel to the remote XBee device.

We need to make a small change to the “serial_read” method. Replace that method with the code below:

    def serial_read(self, buf):
        self.__tracer.debug("Read Data: %s", buf)
        # Update channel
        sample = self.__parse_data(buf)

        self.property_set("lights", Sample(0, value=sample, unit=""))

 

 

This code sends the serial data to a private method called __parse_data that is going to parse the serial data and return a value that we then write to the “lights” channel.

Step 13

We now need to add the method that will parse our serial data:

    def __parse_data(self, buf):
        #append data to the buffer
        #create a var to store the samples
        sample = ""
        self.__rx_buffer += buf
        # check for overflow
        if len(self.__rx_buffer) > MAX_RX_BUFFER:
            try:
                self.__rx_buffer = (
                        self.__rx_buffer[self.__rx_buffer.rindex(':') + 1:])
            except:
                #Throw away everything and only include the latest receive
                self.__rx_buffer = buf

        # advance through the buffer and read the good samples. The while loop will
        # only return the the last good sample since this Arduino will send the status
        # info for every byte it is sent
        while 1:
            eos = self.__rx_buffer.find(':')
            # only read the statements if they end in ':'
            if eos == -1:
                break
            # get a copy of the sentence
            sentence = self.__rx_buffer[:eos]
            #remove the sentence from the receive buffer
            self.__rx_buffer = self.__rx_buffer[eos+1:]
            #check to make sure sentence is the correct length
            if len(sentence) != 5:
                # sentence is too short, skip it
                continue
            sample = sentence

        return sample

 

 

This method does quite a bit of work. First it tries to append the message to the receive buffer. If the message is too large, it tries to find the last complete reading and throws away the rest of the buffer. It then cycles through the buffer and finds the last complete message (ends with “:”) that was 5 characters long and returns it.

Step 14

Now we need to update our dia.yml file to use our new driver. Double-click your dia.yml file to open it. Click the “Source” tab at the bottom of the window to switch from the Graphic editor to the source view.

Step 15

Add an entry for your Arduino device driver just below the xbee_device_manager entry:

  - name: arduino
    driver: custom_devices.xbee_arduino:XBeeArduino
    settings:
        xbee_device_manager: "xbee_device_manager"
        extended_address: "00:13:a2:00:40:76:35:2a!"

Remember to replace the extended address listed above with the extended address of your XBee module.

Step 16

Compile and upload your project to your gateway. I usually select my project in the project explorer, click the build button and manually upload my project and reboot my gateway, but you can click the “Play” button in the toolbar to automatically deploy your code to your gateway.

Step 17: Testing

Now we are going to use the iDigi Dia command line interface to make sure everything is working properly. Use your favorite telnet client and connect to port 4146 on your gateway. If you are using a Mac, just open up terminal and type “telnet (IP ADDRESS) 4146″ where (IP Address) is the local IP address of your gateway. You should see the following message:

Welcome to the iDigi Device Integration Application CLI.
=>>

 

 

First lets check to see if our channels are properly configured and are showing the correct default values. Type in “channel_dump” and hit enter to get a list of your device driver channels. If everything is working correctly, you should see a value of “00000″ for your lights channel.

Now lets send a command. Type the following command at the prompt and press enter to set the value of your write channel:

channel_set arduino.write abc

Now send a second channel_dump command. You should see that the value of your lights channel has changed to “11100″. If you got the correct value, then congratulations! You have just written and deployed a custom iDigi Dia device driver.


Summary

In this article we have seen how to create a custom iDigi Dia device driver to handle reading data from and sending data to remote serial devices. Because we used the iDigi Dia framework, we can use iDigi web services to send commands to the remote device and read any serial data we receive.


On the Blog

— Projects, News & Notes —

48 Hour Day
Art Hack Day brings together technologists, artists, innovators and programmers to produce a series of works, part tech and part art, for a one night only exhibition. You can check out all of the projects at Art Hack Day. More >

Extending the Internet of ANYthing™ — The Iridium satellite network now supports the iDigi Device Cloud, allowing Digi devices with an Iridium data transceiver to send and receive data via the iDigi Device Cloud over the Iridium network. This capability extends connectivity to the remote corners of the globe faster and easier than ever before. More >

You can read more about the iDigi platform,
Digi projects and technology at the iDigi blog
or follow us on Twitter, Facebook and YouTube.
      

Share Your Projects & Ideas — There are lots of great applications that use the iDigi Device Cloud and we want to hear about yours. Contact the team at thunderheads@digi.com to share us your ideas and stories.

Your M2M Expert™for Businessfor Developers
Contact Us
Phone
Email
Chat
Feedback
Newsletter SignupYouTubeGoogle+FacebookCopyright © 1996-2014 Digi International Inc. All rights reserved. Legal