P4A – PHP for Arduino part 2

In the previous post we’ve finished with reading files from SD card. Now we have to pass them through our PHP :) and send the results to the client. To make whole process more simple, we’re assuming each of the files is known to us. This means, we’re registering every file (URL) with addCommand. How it should work? The idea is to keep our functions in the sketch code, whose results should be put into selected places of HTML code. In other words, we want to have HTML file with such a code snippet:

<p>
Sensor 1 reading: MAGIC1<br/>
Sensor 2 reading: MAGIC2<br/>
</p>

Our parser’s goal is to replace MAGIC1 and MAGIC2 with values returned by the functions from the sketch.

It’s best to begin from the magic!

How to write a code in HTML so to tell our parser to substitute it with another text. For simplification we’ll assume the following pattern: #{X} will be substituted with the value returned from appropriate functioni. X is an one-character mnemonic denoting the function we want to invoke. The functions must have a specified definition and mustn’t receive any parameters. Why? Receiving arguments will make the parser unnecessarily complicated and we rather don’t need it now. We’ve already chosen the method of writing entries to HTML code. Now, a question arises, how to pass the returned value from our function? We’re assuming the example function must have the following definition:

void timeReport(char *buf) { itoa(millis()/1000, buf, 10); };

It doesn’t return any values (void), but receives a pointer to a text buffer. In that buffer it has to put its result, and after that, the result will be put in a proper place in the HTML code. The function must take care of the buffer in order not to overload it. Its length is declared in P4A_MAX_FUNCTION_RESPONSE_LENGTH. As you can see, the function above returns the number of seconds elapsed from turning on or resetting the Arduino. How to parse a file? Thanks to our “magic” marker it’s quite easy. We’re reading the file by one character. If we come across ‘#’ character, then we’re reading the next character and checking, if it’s ‘{‘. If does, we have the marker sequence. Until we come across ‘#’, all the characters are sent to the buffer, which will be eventually sent to the web browser. If the next character after ‘#’ isn’t the opening bracket (‘{‘), then both characters (‘#’ and the following) are sent to browser. In case the next character is a bracket, then we’re reading the next character – it’s our mnemonic! After that, a function is invoked, according to the mnemonic, and the result is sent to the web browser. Afterwards, we’re reading the file until the closing bracket (‘}’), then continuing scanning for the next ‘#’. Now it only remains for us to assign functions to mnemonics. For this, we’ll be using

Array of pointers to functions

Let’s talk about slightly more complex topic – pointers to functions. Basically, after compilation, a function is an address at which its code is located along with a contract describing what kind of data is passed to the function and how values are returned from it. If the contract (list of parameters and returned value) is the same for many functions, such a functions can be stored in a form of pointer and kept in an array. Then we are able to call such functions by using the pointer, without a need to know its name. This is what will be serving us as a mechanism translating mnemonics to called functions. Now we know why all of our functions must have the same interface/contract (as we already assigned, it will be void, regarding returned data type and char * as an argument) – thanks to this we are able to keep them in a single array with single-lettered index. But, starting from the beginning:

void (*_fcts['z'-'a'])(char *);

We’re defining an array of pointers to functions. The leading “void” denotes type of data returned by the function, that in brackets means what arguments is expected by the function. Inside is the declaration of an array. It’s name 'z'-'a' can appear strange to you, but in this context the characters are treated as numbers by the compiler. So, from ‘z’ code we’re subtracting ‘a’ code. The difference is the letter count. Thanks to it we have an array which is able to store one pointer for each small letter in Latin alphabet (or rather in ASCII charset). If we have any letter code, it’s enough to subtract the letter code from it and we’ll get the index from the array. Let’s look:

				if (_fcts[c[0]-'a'] == NULL) {
					bufferedSend(server,"n/a");
					continue;
				} else {
					//Call function from table
					_fcts[ c[0]-'a'](buf);
					//Write response to client
					bufferedSend( server, buf );
				}

c[0] contains a character of our mnemonic. If the array don’t have any value (i.e. NULL value) at c[0] - 'a' index, then in HTML code n/a is put – that’s our method of indicating wrong mnemonics. 'a' has a value of 97 in ASCII. If our mnemonic is also 'a' 97-97 = 0, so it’s the first element of the array. If the mnemonic is 'b', then 'b'-'a' = 98 – 97 = 1 so the second element of the array. It would be useful to know if the mnemonic is in range, otherwise we could try to call a function with a random address (when the array index is out of range, RAM memory out of the array will be read and the processor will try to interpret a value at this address as an address of a function to invoke – easy way to crash the program). In the code above you can also see, how to call a function from the array:

_fcts[ c[0]-'a'](buf);

We’re using status variable for file analysis. It indicates the status of the parser. It can be:

  • P4A_PARSE_REGULAR – searching for ‘#’
  • P4A_PARSE_HASH – waiting for the opening bracket (‘{‘)
  • P4A_PARSE_COMMAND – searching for mnemonic

Thanks to the status it’s easier to control, what our parser is doing. According to current status and next letter we can make decision, what to to afterwards. For the interested ones, here is whole parseP4A program code:


//Reads HTML file, parses looking for our macro and sends back to client
int parseP4A( char * filename, WebServer &server ) {
	//simple status
	short int STATUS = P4A_PARSE_REGULAR;
	char c[2];
	c[1] = 0;

	//buffer to hold response from functions - there is no boundary checking, so
	//function has to not overwrite data
  char buf[P4A_MAX_FUNCTION_RESPONSE_LENGTH];

  if (! file.open(&root, filename, O_READ)) {
		return -1;
  }
  while ((file.read(c,1) ) > 0) {
		if (STATUS == P4A_PARSE_REGULAR && c[0] == '#')
		{
			//hash was found we need to inspect next character
			STATUS = P4A_PARSE_HASH;
			continue;
		}

		if (STATUS == P4A_PARSE_HASH) {
			if (c[0] == '{') {
				//go to command mode
				STATUS = P4A_PARSE_COMMAND;
				continue;
			} else {
				//fallback to regular mode, but first send pending hash
				STATUS = P4A_PARSE_REGULAR;
				bufferedSend(server, "#");
			}

		}

		if (STATUS == P4A_PARSE_COMMAND) {
			if(c[0] == '}') {
				STATUS = P4A_PARSE_REGULAR;
				continue;
			};
		  if (c[0] >= 'a' && c[0] <='z') {
				//Command found
				if (_fcts[c[0]-'a'] == NULL) {
					bufferedSend(server,"n/a");
					continue;
				} else {
					//Call function from table
					_fcts[ c[0]-'a'](buf);
					//Write response to client
					bufferedSend( server, buf );
				}
			}

		}

		if (STATUS == P4A_PARSE_REGULAR)
			bufferedSend(server, c);
  }

  //force buffer flushing
  flushBuffer(server);
	file.close();
  return 0;
}

P4A – PHP for Arduino in action

Let’s assume we want to make a nice virtual barometer, but showing real air pressure. We’ll take advantage of BMP085 – it’s a breakout board for pressure and temperature sensor made by SparkFun. We’re going to have the following result:

Virtual barometer's appearance
Virtual barometer's appearance

The hand should point to value read from the sensor and the foretasted weather symbol should change according to air pressure changes. How do we get on with that, when, as it was said in the previous part, WWW server on Arduino is not a best idea (loading many files – pictures(!) simultaneously)? So, if everything is going to be available from the web, why don’t we make the static elements also available from there? These elements are: the barometer’s dial, hand image and weather symbols. On Arduino, on SD card, there’s a HTML file. In the sketch we’re writing a function calling the parser for this file:

void index(WebServer &server, WebServer::ConnectionType type, char *, bool){
  server.httpSuccess();
  if (!parseP4A("BARO.HTM", server)) {
		Serial.println("P4A: -1");
	}
};

parseP4A is a function parsing a given file and sending the result using Webdiuno object. It only remains to register our function as the default command:

webserver.setDefaultCommand(&index);

The HTML itself uses JavaScript and canvas object to draw the dial. This is done by draw function, receiving pressure in hectopascals as an argument. When the site is ready (this means, has been loaded), we’re calling draw function by onload attribute and the pressure is passed by our P4A:

<body onload="draw(#{p});"

p mnemonic has to be assigned with a proper function. In the sketch, in setup, we’re setting a function for p:

 _fcts['p'-'a'] = pressureReport;

And the pressureReport itself looks like:

void pressureReport(char *buf) {
	bmp085_read_temperature_and_pressure (&temperature, &pressure);
	itoa(pressure/100.0, buf, 10);
	Serial.print ("temp & press: ");
	Serial.print (temperature/10.0);
	Serial.print(" ");
	Serial.println (pressure/100.0);

};

Serial is used for checking, if the values are according to our expectations and there isn’t any impact on the barometer’s operation. bmp085_read_temperature_and_pressure it’s a BMP085 handling function, taken from a finnish blog. The whole code is available for download here. It’s a sketch powering our server plus HTML file and graphics. The dial, graphics and HTML/JS code was made bySprae.

Installation

Download, unpack in sketchbook. Content of html subdirectory has to be put into the main catalog on SD card, then insert the card into the shield. Correct the sketch by specifying proper MAC and IP address. After opening the main site in our browser, we should see the barometer, supposing we have BMP085 ;)

Some annotations at the end

The code is beta version :) It means it worked for me, during the tests I performed and maybe there’s some bugs (and probably that’s true) I haven’t ever imagined :) The code needs to be rearranged, e.g. functions related to buffered output should be moved to Webdiuno code. I’m planning to make it and submit all the changes to Webduino developers. Maybe some of them will be included in the library. I’m curious of your opinion. Does something like P4A have any sense and could it be useful?