Possible to create XML with Arduino?

Hey hello everybody, i got a question for my arduino. This is the plan:
i have some inputs from my robot, (when its doing a movement in wich direction etc) and i want to log it on a SD-card. Is it also possible to create on the arduino a XML-file and send it via Ethernet?
Thanks,Wouternet

It is technically possible

It’s definitely possible. I’m doing the same thing, except i monitor light and temperature sensors and send back an xml document with each reading over a socket to a program running on a pc that parses it and stores it into mySQL.

I ran into a few problems along the way though. I got answers here on the forum.

The first is that strings take up a lot of memory and writing out XML tags uses a lot of strings. You have to push them into program memory. Check out this post on how to do this.

http://www.arduino.cc/en/Reference/PROGMEM

Once I did this, it started working better, but would still hang after a few connections. I found this post about a bug in the Client class. I just made this change to the library and will test it tomorrow, but it seems like a fix to my problem.

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1227735530/5

Memory, again, is a big issue. With the Ethernet library added in, your at about 13k with very little code. So, you don’t have much room to work in and who knows how much RAM this eats up.

Michael

Here’s the code if you’re interested. This works great for serial and for ethernet, except for the hanging problem i described. i’ll find out tomorrow if that’s fixed.

#include <avr/pgmspace.h>

#include <Client.h>
#include <Ethernet.h>

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#define SERVER_PORT 14822
#define NUMBER_SAMPLES 8

#define boardId 2

byte mac = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip = { 192, 168, 200, 200 };
byte gateway = { 192, 168, 200, 1 };
byte netmask = { 255, 255, 255, 0 };
byte server = { 192, 168, 200, 110 };

// temperature readings interval in seconds.
#define reportInterval 60

unsigned long startTime = 0;
unsigned long minuteTimer = 0;
#define NO_SENSOR 0
#define SENSOR_TEMP 1
#define SENSOR_LIGHT 2

#define NUMBER_SENSORS 6
int sensors[NUMBER_SENSORS];

void
ConvertInt(char *data, int value, int size)
{
int length;
if ( size == 0 )
{
if ( value == 0 )
length = 1;
else
{
int newvalue = value;
length = 0;
while ( newvalue > 0 )
{
newvalue /= 10;
length++;
}
}
}
else
length = size;
for ( int i = 0; i < length; i++ )
{
int mod = value % 10;
data[length - i - 1] = (char)mod + ‘0’;
value = value / 10;
}
data[length] = ‘\0’;
}

int
ConvertDouble( char *data, double value, int decimalPlaces)
{
char wholeString[10];
char fractionString[10];
ConvertInt(wholeString, (int)value, 0);

double dec = value - (double)((int)value);
for ( int i = 0; i < decimalPlaces; i++ )
dec = dec * 10.0;

ConvertInt(fractionString, (int)dec, decimalPlaces);

int n = 0;
for (n = 0; n < wholeString[n] != ‘\0’; n++)
data[n] = wholeString[n];
data[n++] = ‘.’;
for (int i = 0; fractionString != ‘\0’; i++)
_ data[n++] = fractionString*;_
_
data[n] = ‘\0’;_
_
}_
double ReadValue( int sensorId )
_
{_
_
double value = 0.0;_
_
//_
_
// Read a number of samples and average them to get a more accurate value._
_
//_
for (int i = 0; i < NUMBER_SAMPLES; i++)
_
value += (double)analogRead(sensorId);_
value = value / (double)NUMBER_SAMPLES;
_
return value;_
_
}_
double
ReadTemp(int sensorId)
_
{_
_
double tempc = ReadValue(sensorId);_
_
// The temperate is 10mv per degree, 1024 is 5.0V, so scale this to degress._
_ tempc = ( 5.0 * tempc * 100.0) / 1024.0;_
_
//_
_
// Convert to Fahrenheit*_
_ return ((tempc * 9.0)/5.0) + 32.0;
}
PROGMEM const prog_char xmlStartTag[] = “<?xml version=\"1.0\" encoding=\"utf-8\"?>”;
PROGMEM const prog_char xmlEndTag[] = “</?xml?>”;
PROGMEM const prog_char messageStartTag[] = “”;
PROGMEM const prog_char messageEndTag[] = “”;
PROGMEM const prog_char dataStartTag[] = " ";
PROGMEM const prog_char dataEndTag[] = " ";
PROGMEM const prog_char readingStartTag[] = " ";
PROGMEM const prog_char readingEndTag[] = “”;
PROGMEM const prog_char locationStartTag[]= " ";
PROGMEM const prog_char locationEndTag[] = " ";
PROGMEM const prog_char boardStartTag[] = " ";
PROGMEM const prog_char boardEndTag[] = “”;
PROGMEM const prog_char sensorStartTag[] = " ";
PROGMEM const prog_char sensorEndTag[] = “”;
PROGMEM const prog_char scaleLine[] = " F";
PROGMEM const prog_char tempTypeLine[] = " Temperature";
PROGMEM const prog_char lightTypeLine[] = " Light";
PROGMEM const prog_char messageStartTag1[] = “<Message”;
PROGMEM const prog_char messageStartTag2[] = " xmlns=“urn:sol.bosque.com:Solar:Arduino”";
PROGMEM const prog_char messageStartTag3[] = " xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”";
PROGMEM const prog_char messageStartTag4[] = " xsi:schemaLocation="urn:sol.bosque.com:Solar:Arduino ";
PROGMEM const prog_char messageStartTag5[] = "http://winserv/Schemas/ArduinoData.xsd">";
PGM_P StringTable[] PROGMEM =
*{ *
* xmlStartTag,*
* xmlEndTag,*
* messageStartTag,*
* messageEndTag,*
* dataStartTag,*
* dataEndTag,*
* readingStartTag,*
* readingEndTag,*
* locationStartTag,*
* locationEndTag,*
* messageStartTag1,*
* messageStartTag2,*
* messageStartTag3,*
* messageStartTag4,*
* messageStartTag5,*
* boardStartTag,*
* boardEndTag,*
* sensorStartTag,*
* sensorEndTag,*
* scaleLine,*
* tempTypeLine,*
* lightTypeLine, *
};
#define XML_START_TAG 0
#define XML_END_TAG 1
#define MESSAGE_START_TAG 2
#define MESSAGE_END_TAG 3
#define DATA_START_TAG 4
#define DATA_END_TAG 5
#define READING_START_TAG 6
#define READING_END_TAG 7
#define LOCATION_START_TAG 8
#define LOCATION_END_TAG 9
#define MESSAGE_START_TAG1 10
#define MESSAGE_START_TAG2 11
#define MESSAGE_START_TAG3 12
#define MESSAGE_START_TAG4 13
#define MESSAGE_START_TAG5 14
#define BOARD_START_TAG 15
#define BOARD_END_TAG 16
#define SENSOR_START_TAG 17
#define SENSOR_END_TAG 18
#define SCALE_LINE 19
#define TEMP_TYPE_LINE 20
#define LIGHT_TYPE_LINE 21
void
WriteString(Print *stream, int id, int newline)
{
* char buffer[80];*
strcpy_P(buffer, (char*)pgm_read_word(&(StringTable[id])));
* if ( newline == 0 )*
* stream->print(buffer);*
* else*
* stream->println(buffer);*
}
void
WriteMessageHeader(Print *stream )
{
* WriteString(stream, XML_START_TAG, TRUE);
WriteString(stream, MESSAGE_START_TAG1, TRUE);
WriteString(stream, MESSAGE_START_TAG2, TRUE);
WriteString(stream, MESSAGE_START_TAG3, TRUE);
WriteString(stream, MESSAGE_START_TAG4, FALSE);
WriteString(stream, MESSAGE_START_TAG5, TRUE);
_
}*

void
WriteMessageEnd(Print *stream)
{
* WriteString(stream, MESSAGE_END_TAG, TRUE);
WriteString(stream, XML_END_TAG, TRUE);
_
}_
void
WriteLocation(Print *stream, int sensorId)
_
{_
WriteString( stream, LOCATION_START_TAG, TRUE);
WriteString( stream, BOARD_START_TAG, FALSE);
_
stream->print(boardId);_
WriteString( stream, BOARD_END_TAG, TRUE);
WriteString( stream, SENSOR_START_TAG, FALSE);
_
stream->print(sensorId);_
WriteString( stream, SENSOR_END_TAG, TRUE);
WriteString( stream, LOCATION_END_TAG, TRUE);
_
}_
void WriteLight(Print *stream, int id)
_
{_
_
int time = (millis() - startTime)/1000;_
_
WriteMessageHeader(stream);_
WriteString(stream, DATA_START_TAG, TRUE);
WriteString(stream, READING_START_TAG, FALSE);
_
stream->print((int)ReadValue(id));_
WriteString(stream, READING_END_TAG, TRUE);
WriteString(stream, LIGHT_TYPE_LINE, TRUE);
_
WriteLocation(stream, id);_
WriteString(stream, DATA_END_TAG, TRUE);
_
WriteMessageEnd(stream);_
_
}_
void WriteTemperature(Print *stream, int id)
_
{_
_
int time = (millis() - startTime)/1000;_
_
double temp = ReadTemp(id);*_

* if ( temp > 1000.00)*
* temp = 500.0;*

* WriteMessageHeader(stream);*
* WriteString(stream, DATA_START_TAG, TRUE);
WriteString(stream, READING_START_TAG, FALSE);*

* char s[10];*
* ConvertDouble(s, temp, 2);*
* stream->print(s);*

* WriteString(stream, READING_END_TAG, TRUE);
WriteString(stream, SCALE_LINE, TRUE);
WriteString(stream, TEMP_TYPE_LINE, TRUE);
_
WriteLocation(stream, id);_
WriteString(stream, DATA_END_TAG, TRUE);
_
WriteMessageEnd(stream);_
_
}_
void WriteSensors()
_
{_
Client client( server, SERVER_PORT);
_
int connected = 0;_
_
if (client.connect() != 0)_
_
{_
_
connected = 1;_
_
}_
for ( int i = 0; i < NUMBER_SENSORS; i++)
_
{_
_ switch ( sensors )
{_
case SENSOR_TEMP:
_ WriteTemperature(&Serial, i);
if (connected == 1)
WriteTemperature(&client, i);
break;_
case SENSOR_LIGHT:
_ WriteLight( &Serial, i);
if (connected == 1)
WriteLight(&client, i);
break;
default:
break;
}
}
if (connected == 1)
{
client.stop();
}*_

}
void setup()
*{ *
* Serial.begin(115200);*
* Ethernet.begin( mac, ip, gateway, netmask );*

* startTime = millis();*
* minuteTimer = millis();*

* for ( int i = 0; i < NUMBER_SENSORS; i++)
_ {
sensors = NO_SENSOR;
}_

sensors[1] = SENSOR_TEMP;
sensors[2] = SENSOR_TEMP;
sensors[3] = SENSOR_TEMP;
sensors[5] = SENSOR_LIGHT;
_ WriteSensors();
}
void loop()
{
unsigned long delta = ( millis() - minuteTimer ) / 1000;
if ( delta > reportInterval )
{
minuteTimer = millis();
WriteSensors();
}
}*_

Suggestion,
instead of defining both the beginning and end tags:
PROGMEM const prog_char messageStartTag = “”;
PROGMEM const prog_char messageEndTag = “”;

define the tag “Message” and make writeStartTag(Stream, const prog_char *), writeEndTag(Stream, const prog_char *) functions to add the “<>” or the “</>” around the string. Probably save quite a bit of flash that way.

Great suggestion. That would simplify things quite a bit and save memory.

Michael

@joquer
in another topic i’ve read that you do your communication to the arduino using a .NET-application.
Because my efforts are the same (actually i’m using the serial communication via USB) and i was searching a solution to use ethernet. I simply tried your listing above (adjusted to my IP-range ) but no .NET approach was able to connect to the ethernet shield.
The serial port delivered the correct XML statements. Pinging was no problem and the firewall was switched off.

I’m using the “official Ethernet shield” with Wiz5100 and an Arduino with Atmega328. The Arduino release is 15.
Could you tell me your secret?

Thanks Klaus

You could perhaps use a PHP page that stores the data when you call it with GET. But it doesn’t use the SD!