Adventures with an Arduino – Part 4: Fun With Frames.

“Welcome to Sheldon Cooper Presents Fun with Flags.” – Sheldon Cooper.

Ok, whilst we’re not studying vexillology, the title of this post reminded me of the hilarious Fun with Flags webcast from The Big Bang Theory. In our case, it’s actually Fun with Frames, the frames of course being those of our good friend Mr Ethernet. We did after all buy the Ethernet Arduino board.

This post is part of a 6 part series, starting with Part 1.

This is where we actually start needing to pay a bit more attention to the code. Given the solution was to use a web page on the phone to control the garage door, I needed to host a web page somewhere. One option would have been to develop a web server on the Arduino and serve pages from there directly. However, as I have a web server already up and running at home, I decided to use that as my hosting platform. Creating the web page was relatively simple, I used ASP.NET with C#, an iPhone stylesheet so that the web page on the phone has the Apple look and feel to it (very important during user acceptance testing with my wife), and an authentication cookie with a relatively long lifetime. This allows us to log in to the page on the phone, and then use an authentication cookie to avoid re-authenticating every time we use the page in the future. I won’t focus on the web page here (there’re many examples of how to create an ASP.NET application online).

So, now that the web page has been created, I needed to decide which protocol to use between the web server and the Arduino. Fundamentally, this means IPv4 TCP or UDP. My original plan was to create some REST API functions on the Arduino, but I decided that I didn’t need to add that level of complexity just yet. TCP would have been good if I actually required a connection, or protocol guaranteed packet delivery; however for my purposes UDP was sufficiently light-weight.

#define PIN_GARAGE_TRIGGER 7
#define UDP_LISTEN_PORT 46007

#include <SPI.h>
#include <Ethernet.h>

EthernetUDP ethUDP;
boolean bNetwork = false;
// Use MAC Address supplied with Arduino
byte btMAC[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };

/***************************************************************/
/* Function: setup()                                           */
/***************************************************************/
void setup()
{
	// Initialise Serial Port Communications (via USB)
	Serial.begin(9600);

	Serial.println("setup()");

	// Configure Pins
	pinMode(PIN_GARAGE_TRIGGER, OUTPUT);
}

/***************************************************************/
/* Function: startNetwork()                                    */
/***************************************************************/
void startNetwork()
{
		Serial.println("startNetwork()");

		// Attempt to initialize ethernet port and receive DHCP address
		if (Ethernet.begin(btMAC) == 1)
		{
				bNetwork = true;

				Serial.println("IP Address: " + String(Ethernet.localIP().toString()));

				ethUDP.begin(UDP_LISTEN_PORT);
		}
		else
				Serial.println("Could not assign DHCP address");
}

/***************************************************************/
/* Function: loop()                                            */
/***************************************************************/
void loop()
{
	IPAddress ipClient;
	int iBytesReceived = 0;
	char szBuffer[30];
	String strBuffer;

	Serial.println("loop()");

	// Start Network
	if (!bNetwork)
		startNetwork();

	if (!bNetwork)
		return;

	iBytesReceived = ethUDP.parsePacket();
	// Packet Received
	if (iBytesReceived > 0)
	{
		ipClient = ethUDP.remoteIP();

		// Clear Buffer
		memset(szBuffer, 0, sizeof(szBuffer));

		ethUDP.read(szBuffer, sizeof(szBuffer));

		// Confirm buffer is null-terminated
		if (szBuffer[sizeof(szBuffer) - 1] == 0)
		{
  			// Convert (char *) to (String). The String class has more functionality
			strBuffer = String(szBuffer);

			// Trigger Garage Door
			if (strBuffer == "SignalGarageTrigger")
			{
				Serial.println("Received trigger packet from " + String(ipClient.toString()) + ":" + ethUDP.remotePort());

				// Generate and Send response packet
				ethUDP.beginPacket(ethUDP.remoteIP(), ethUDP.remotePort());
				ethUDP.write("Garage Door Triggered");
				ethUDP.endPacket();

				// Trigger Relay
				digitalWrite(PIN_GARAGE_TRIGGER, HIGH);

				delay(200);

				digitalWrite(PIN_GARAGE_TRIGGER, LOW);
			}
			// Unknown Message
			else
			{
				Serial.println("Unknown packet from " + String(ipClient.toString()) + ":" + ethUDP.remotePort() + " (" + strBuffer + ")");
			}
		}
		// Corrupt Message
		else
		{
			Serial.println("Corrupt packet from " + String(ipClient.toString()) + ":" + ethUDP.remotePort());
		}
	}

	delay(50);
}

The code’s rather lengthy this time. If you’re familiar with C/C++ the logic should be fairly straight forward. I used the reference material on the Arduino web site to learn how the Ethernet classes operate.

I did have one problem however. For some bizarre reason, the default implementation of the Arduino IPAddress class does not have a simple way of extracting a text based representation of the IP Address (dotted decimal). As such, I made several changes to the IPAddress.cpp and IPAddress.h files in the base Arduino code set (found in a subfolder of the Arduino Folder \hardware\arduino\avr\cores\arduino).

Here are the changes:

IPAddress.h – just the final line.

public:
    // Constructors
    IPAddress();
    IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
    IPAddress(uint32_t address);
    IPAddress(const uint8_t *address);
    char *toString();

IPAddress.cpp – new function added to the bottom of the file.

char *IPAddress::toString()
{
    char szIPAddress[20];

    memset(szIPAddress, 0, sizeof(szIPAddress));

    sprintf(szIPAddress, "%u.%u.%u.%u", _address[0], _address[1], _address[2], _address[3]);

    return szIPAddress;
}

Interestingly enough, when I first created this code I used the String class (String IPAddress::toString()). However, when I attempted to use the WiFi library in a future experiment, I was having compiler errors due to the WiFi class loading some headers out of order resulting in the String class not being recognised. I initially fixed it, but after deciding to try keep my changes to their code to a minimum, to avoid maintenance every time I upgrade the IDE, I reverted back to a character array (char *IPAddress::toString()), but I didn’t rename the function to toCharArray(). This works perfectly, it just means I generally need to convert the output to a String before using it.

Now, before anyone points out the foolishness of defining a local variable (szIPAddress) and then returning a pointer to it to the calling function, there is an explanation for my choice. Typically, you would not return a pointer from a function, to a local variable. Why? The variable will potentially disappear as soon as the function concludes as the variable has now gone out of scope. This of course depends on the run-time environment, garbage collection etc. However, given the nature of the Arduino and how the code actually executes, returning a pointer to a character array doesn’t cause me any grief because I’m immediately capturing the data in the variable and placing it inside a String variable.

{
    Serial.println("IP Address: " + String(ipAddress.toString()));
}

The String() constructor automatically copied the contents of the char * array into a new block of memory referenced by the String class. So even though the original char * array will disappear soon, I’ve already captured the output. With devices like the Arduino, whilst we try and maintain standard programming rules, given the lack of memory and multi-threadedness, some of these rules can be bent in the name of expediency. Either way, I’m looking to get a proper enhancement done to the base Arduino code and included in the standard libraries.

I’ve kept the code simple in this example. The real version actually sent syslog messages to a syslog server so that I could track what the device was doing. The Serial.println() function doesn’t work as well once the module has been deployed and is no longer connected to a serial/USB port.

Now we have the networking code and the breadboard circuit. Time to build.

On to Part 5.

~ Mike

2 thoughts on “Adventures with an Arduino – Part 4: Fun With Frames.

  1. hi Mike
    I’ve put this modifications into IPAddress. then I’ve added one line to the ethernet example “webserver.ino” and get the following output:
    server is at 192.168.1.102
    IP Address (new function): R Öæ
    — modifierd setup code:
    // start the Ethernet connection and the server:
    Ethernet.begin(mac);
    server.begin();
    Serial.print(“server is at “);
    Serial.println(Ethernet.localIP());
    Serial.println(“IP Address (new function): ” + String(Ethernet.localIP().toString()));

    tested on Arduino duemilanove, original Ethernet shield, Arduino 1.0.5-r2

    thanks for your analysis and help
    urs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s