0.2.2 major updates XMLWriter

This commit is contained in:
rob tillaart 2020-05-25 09:58:04 +02:00
parent 6680ecdffd
commit 5b156b7099
17 changed files with 1202 additions and 148 deletions

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2020 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,35 @@
# XMLWriter
Arduino Library to create simple XML (messages, files, print, ...)
## Description
The XMLWriter class supports generating XML files and send these over a stream
like Ethernet SD.File or Serial.
When instantiating an XMLWriter one can define the internal buffer size.
This buffering makes the output faster, especially for Ethernet and SD.File.
The buffer size should be at least 2 bytes.
A bigger buffer is often faster but it also depends on the properties of the
stream to see real performance gains.
E.g. the baudrate and internal buffer of Serial, packet behaviour of Ethernet,
or paging of SD cards.
So if performance is an issue one should do some testruns with different sizes
for the buffer and choose one that is appropriate.
Indicative sizes based upon the examples.
Run your tests to find your application optimum.
| STREAM | SIZE |
|:------------|:----------|
| Ethernet | 20-30 |
| Serial | 5 |
| SD File | 10-16 |
Important to know when usinig buffering is that your code should always include
a call to **XML.flush()** at the end of the XML generation to flush the buffer.
## Operation
See examples

View File

@ -1,9 +1,9 @@
//
// FILE: XMLWriter.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.8
// VERSION: 0.2.2
// DATE: 2013-11-06
// PURPOSE: Simple XML library
// PURPOSE: Arduino library for creating XML
//
// HISTORY:
// 0.1.00 2013-11-06 initial version
@ -15,39 +15,62 @@
// 0.1.6 2016-03-16 added incrIndent(), decrIndent(), indent(), raw();
// 0.1.7 2017-07-26 added const where possible
// 0.1.8 2017-12-09 fix casting issue #83 (long -> int32_t);
//
// Released to the public domain
// 0.1.9 2017-12-09 add PROGMEM support for escape() strings
// 0.2.0 2020-04-24 refactor, added examples, #pragma, print as base class
// 0.2.1 2020-04-26 performance optimized, setconfig() + newLine() added
// 0.2.2 2020-04-29 dynamic buffer size in constructor
//
#include <XMLWriter.h>
XMLWriter::XMLWriter(Print* stream)
XMLWriter::XMLWriter(Print* stream, uint8_t bufsize)
{
_bufsize = constrain(bufsize, 2, 250);
_buffer = (char *) malloc(_bufsize);
_stream = stream;
reset();
}
XMLWriter::~XMLWriter()
{
if (_buffer != NULL) free(_buffer);
}
void XMLWriter::reset()
{
_indent = 0;
_indentStep = 2;
_idx = 0;
_tidx = 0;
_bidx = 0;
_config = XMLWRITER_COMMENT | XMLWRITER_INDENT | XMLWRITER_NEWLINE;
_bytesOut = 0;
}
void XMLWriter::header()
{
_stream->println(F("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"));
print(F("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"));
}
void XMLWriter::comment(const char* text, const bool multiLine)
{
_stream->println();
if (!multiLine) indent();
_stream->print(F("<!-- "));
if (multiLine) _stream->println();
_stream->print(text);
if (multiLine) _stream->println();
_stream->println(F(" -->"));
if (_config & XMLWRITER_COMMENT)
{
print('\n');
if (!multiLine) indent();
print(F("<!-- "));
if (multiLine) print('\n');
print(text);
if (multiLine) print('\n');
print(F(" -->\n"));
}
}
void XMLWriter::newLine(uint8_t n)
{
if (_config & XMLWRITER_NEWLINE)
{
while(n--) print('\n');
}
}
void XMLWriter::tagOpen(const char* tag, const bool newline)
@ -57,8 +80,15 @@ void XMLWriter::tagOpen(const char* tag, const bool newline)
void XMLWriter::tagOpen(const char* tag, const char* name, const bool newline)
{
// TODO STACK GUARD
strncpy(tagStack[_idx++], tag, XMLWRITER_MAXTAGSIZE);
if (_tidx > XMLWRITER_MAXLEVEL)
{
comment("XMLWRITER_MAXLEVEL exceeded.");
comment(tag);
comment(name);
return;
}
// "unsafe" strcpy saves ~20 bytes
strncpy(_tagStack[_tidx++], tag, XMLWRITER_MAXTAGSIZE);
tagStart(tag);
if (name[0] != 0) tagField("name", name);
tagEnd(newline, NOSLASH);
@ -69,36 +99,37 @@ void XMLWriter::tagClose(const bool ind)
{
_indent -= _indentStep;
if (ind) indent();
_stream->print(F("</"));
_stream->print(tagStack[--_idx]);
_stream->println(F(">"));
print("</");
print(_tagStack[--_tidx]);
print(">\n");
delay(10);
}
void XMLWriter::tagStart(const char *tag)
{
indent();
_stream->print('<');
_stream->print(tag);
print('<');
print(tag);
}
void XMLWriter::tagField(const char *field, const char* str)
{
_stream->print(' ');
_stream->print(field);
_stream->print(F("=\""));
print(' ');
print(field);
print("=\"");
#ifdef XMLWRITER_ESCAPE_SUPPORT
escape(str);
#else
_stream->print(str);
print(str);
#endif
_stream->print('"');
print('"');
}
void XMLWriter::tagEnd(const bool newline, const bool addSlash)
{
if (addSlash) _stream->print('/');
_stream->print('>');
if (newline) _stream->println();
if (addSlash) print("/>");
else print('>');
if (newline) print('\n');
}
void XMLWriter::writeNode(const char* tag, const char* str)
@ -107,7 +138,7 @@ void XMLWriter::writeNode(const char* tag, const char* str)
#ifdef XMLWRITER_ESCAPE_SUPPORT
escape(str);
#else
_stream->print(str);
print(str);
#endif
tagClose(NOINDENT);
}
@ -129,11 +160,11 @@ void XMLWriter::tagField(const char *field, const uint16_t value, const uint8_t
void XMLWriter::tagField(const char *field, const uint32_t value, const uint8_t base)
{
_stream->print(' ');
_stream->print(field);
_stream->print(F("=\""));
_stream->print(value, base);
_stream->print('"');
print(' ');
print(field);
print("=\"");
print(value, base);
print('"');
}
void XMLWriter::tagField(const char *field, const int8_t value, const uint8_t base)
@ -148,30 +179,28 @@ void XMLWriter::tagField(const char *field, const int16_t value, const uint8_t b
void XMLWriter::tagField(const char *field, const int32_t value, const uint8_t base)
{
_stream->print(' ');
_stream->print(field);
_stream->print(F("=\""));
_stream->print(value, base);
_stream->print('"');
print(' ');
print(field);
print("=\"");
print(value, base);
print('"');
}
void XMLWriter::tagField(const char *field, const bool value)
{
_stream->print(' ');
_stream->print(field);
_stream->print(F("=\""));
_stream->print(value?F("true"):F("false"));
_stream->print('"');
print(' ');
print(field);
// F() is slower & uses less RAM but 15 bytes saved
print(value ? F("=\"true\"") : F("=\"false\""));
}
void XMLWriter::tagField(const char *field, const double value, const uint8_t decimals)
{
_stream->print(' ');
_stream->print(field);
_stream->print(F("=\""));
_stream->print(value, decimals);
_stream->print('"');
print(' ');
print(field);
print(F("=\""));
print(value, decimals);
print('"');
}
void XMLWriter::writeNode(const char* tag, const uint8_t value, const uint8_t base)
@ -187,7 +216,7 @@ void XMLWriter::writeNode(const char* tag, const uint16_t value, const uint8_t b
void XMLWriter::writeNode(const char* tag, const uint32_t value, const uint8_t base)
{
tagOpen(tag, "", NONEWLINE);
_stream->print(value, base); // todo: leading zero's
print(value, base);
tagClose(NOINDENT);
}
@ -204,46 +233,106 @@ void XMLWriter::writeNode(const char* tag, const int16_t value, const uint8_t ba
void XMLWriter::writeNode(const char* tag, const int32_t value, const uint8_t base)
{
tagOpen(tag, "", NONEWLINE);
_stream->print(value, base); // todo: leading zero's
print(value, base);
tagClose(NOINDENT);
}
void XMLWriter::writeNode(const char* tag, const bool value)
{
tagOpen(tag, "", NONEWLINE);
_stream->print(value?F("true"):F("false"));
// F() is slower & uses less RAM but saves 9 bytes
print(value ? F("true") : F("false"));
tagClose(NOINDENT);
}
void XMLWriter::writeNode(const char* tag, const double value, const uint8_t decimals)
{
tagOpen(tag, "", NONEWLINE);
_stream->print(value, decimals);
print(value, decimals);
tagClose(NOINDENT);
}
void XMLWriter::indent()
{
for (uint8_t i=_indent; i>0; i--) _stream->print(' ');
if (_config & XMLWRITER_INDENT)
{
// as indentation is a multiple of 2
// this is nice balance between speed and RAM.
for (uint8_t i = _indent; i > 0; i-= 2) print(" ");
}
}
size_t XMLWriter::write(uint8_t c)
{
_buffer[_bidx++] = c;
if (_bidx == (_bufsize - 1)) flush();
return 1;
};
void XMLWriter::flush()
{
_bytesOut += _bidx;
if (_bidx > 0)
{
_buffer[_bidx] = 0;
_stream->print(_buffer);
_bidx = 0;
}
};
////////////////////////////////////////////////////////////////////
//
// ESCAPE
//
#ifdef XMLWRITER_ESCAPE_SUPPORT
char c[6] = "\"\'<>&";
char expanded[][7] = { "&quot;", "&apos;","&lt;","&gt;","&amp;"}; // todo in flash
static char c[6] = "\"\'<>&";
#ifdef __PROGMEM__
PROGMEM const char quote[] = "&quot;";
PROGMEM const char apostrophe[] = "&apos;";
PROGMEM const char lessthen[] = "&lt;";
PROGMEM const char greaterthen[] = "&gt;";
PROGMEM const char ampersand[] = "&amp;";
PROGMEM const char* const expanded[] =
{
quote, apostrophe, lessthen, greaterthen, ampersand
};
#else
// NOTE: & and ; are handled in code. // 25 bytes RAM
static char expanded[][5] = { "quot", "apos","lt","gt","amp"};
#endif
void XMLWriter::escape(const char* str)
{
char* p = (char *)str;
while(*p != 0)
while (*p != 0)
{
char* q = strchr(c, *p);
if (q == NULL) _stream->print(*p);
else _stream->print(expanded[q - c]); // uint8_t idx = q-c;
if (q == NULL) print(*p);
#ifdef __PROGMEM__
else
{
char buf[8];
strcpy_P(buf, (char*)pgm_read_word(&(expanded[q - c])));
print(buf);
}
#else
else
{
print('&');
print(expanded[q - c]); // uint8_t idx = q-c;
print(';');
}
#endif
p++;
}
}
#endif
// END OF FILE
// -- END OF FILE --

View File

@ -1,19 +1,15 @@
#ifndef XML_WRITER_H
#define XML_WRITER_H
#pragma once
//
// FILE: XMLWriter.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.8
// VERSION: 0.2.2
// DATE: 2013-11-06
// PURPOSE: Simple XML writer library
//
// Released to the public domain
// PURPOSE: Arduino library for creating XML
//
#include "Arduino.h"
// no pre 1.0 support!
#define XMLWRITER_VERSION "0.1.8"
#define XMLWRITER_VERSION "0.2.2"
// for comment()
#define NOMULTILINE false
@ -30,88 +26,129 @@
// deepness of XML tree 5..10
// needed for stack of tagStack
#ifndef XMLWRITER_MAXLEVEL
#define XMLWRITER_MAXLEVEL 5 // adjust for deeper nested structures
#define XMLWRITER_MAXTAGSIZE 15 // adjust for longer fields - !! eats memory !!
#endif
#ifndef XMLWRITER_MAXTAGSIZE
#define XMLWRITER_MAXTAGSIZE 15 // adjust for longer fields - !! eats memory !!
#endif
// reduce footprint by commenting next line
#define XMLWRITER_ESCAPE_SUPPORT
class XMLWriter
// configuration - setConfig
#define XMLWRITER_NONE 0x00
#define XMLWRITER_COMMENT 0x01
#define XMLWRITER_INDENT 0x02
#define XMLWRITER_NEWLINE 0x04
// uncomment next line to reduce ~30bytes RAM in escape() (AVR oonly)
// #define __PROGMEM__
class XMLWriter : Print
{
public:
XMLWriter(Print* stream);
// default = Serial
XMLWriter(Print* stream = &Serial, uint8_t bufsize = 10);
~XMLWriter();
void reset();
// to show/strip comment, indent, newLine
// to minimize the output setConfig(0);
void setConfig(uint8_t cfg) { _config = cfg; };
// standard XML header
void header();
// if multiline == true it does not indent to allow bigger text blocks
// <!-- text -->
void comment(const char* text, const bool multiLine=false);
void comment(const char* text, const bool multiLine = false);
// add a number of newlines to the output, default = 1.
void newLine(uint8_t n = 1);
// <tag>
void tagOpen(const char* tag, const bool newline=true);
void tagOpen(const char* tag, const bool newline = true);
// <tag name="name">
void tagOpen(const char* tag, const char* name, const bool newline=true);
void tagOpen(const char* tag, const char* name, const bool newline = true);
// </tag>
void tagClose(const bool ind=true);
void tagClose(const bool ind = true);
// <tag
void tagStart(const char* tag);
// field="value"
void tagField(const char* field, const char* value);
// />
void tagEnd(const bool newline=true, const bool addSlash=true);
void tagEnd(const bool newline = true, const bool addSlash = true);
// <tag>value</tag>
void writeNode(const char* tag, const char* value);
// typically 0,2,4; default == 2;
// multiple of 2;
void setIndentSize(const uint8_t size = 2);
// for manual layout control
void incrIndent() { _indent += _indentStep; };
void decrIndent() { _indent -= _indentStep; };
void indent();
void raw(const char * str) { _stream->print(str); }; // TODO Q:other types?
void raw(const char * str) { print(str); };
void tagField(const char* field, const uint8_t value, const uint8_t base=DEC);
void tagField(const char* field, const uint16_t value, const uint8_t base=DEC);
void tagField(const char* field, const uint32_t value, const uint8_t base=DEC);
void tagField(const char* field, const int8_t value, const uint8_t base=DEC);
void tagField(const char* field, const int16_t value, const uint8_t base=DEC);
void tagField(const char* field, const int32_t value, const uint8_t base=DEC);
void tagField(const char *field, const bool value);
void tagField(const char* field, const double value, const uint8_t decimals=2);
void tagField(const char* field, const uint8_t value, const uint8_t base = DEC);
void tagField(const char* field, const uint16_t value, const uint8_t base = DEC);
void tagField(const char* field, const uint32_t value, const uint8_t base = DEC);
void tagField(const char* field, const int8_t value, const uint8_t base = DEC);
void tagField(const char* field, const int16_t value, const uint8_t base = DEC);
void tagField(const char* field, const int32_t value, const uint8_t base = DEC);
void tagField(const char* field, const bool value);
void tagField(const char* field, const double value, const uint8_t decimals = 2);
void writeNode(const char* tag, const uint8_t value, const uint8_t base=DEC);
void writeNode(const char* tag, const uint16_t value, const uint8_t base=DEC);
void writeNode(const char* tag, const uint32_t value, const uint8_t base=DEC);
void writeNode(const char* tag, const int8_t value, const uint8_t base=DEC);
void writeNode(const char* tag, const int16_t value, const uint8_t base=DEC);
void writeNode(const char* tag, const int32_t value, const uint8_t base=DEC);
void writeNode(const char* tag, const uint8_t value, const uint8_t base = DEC);
void writeNode(const char* tag, const uint16_t value, const uint8_t base = DEC);
void writeNode(const char* tag, const uint32_t value, const uint8_t base = DEC);
void writeNode(const char* tag, const int8_t value, const uint8_t base = DEC);
void writeNode(const char* tag, const int16_t value, const uint8_t base = DEC);
void writeNode(const char* tag, const int32_t value, const uint8_t base = DEC);
void writeNode(const char* tag, const bool value);
void writeNode(const char* tag, const double value, const uint8_t decimals=2);
void writeNode(const char* tag, const double value, const uint8_t decimals = 2);
#ifdef XMLWRITER_ESCAPE_SUPPORT
// expands the special xml chars
void escape(const char* str);
#endif
// One need to call flush() at the end of writing to empty
// the internal buffer.
void flush();
// metrics
uint8_t bufferIndex() { return _bidx; };
uint32_t bytesWritten() { return _bytesOut; } ;
private:
// outputstream
Print* _stream;
// outputstream, Print Class
Print* _stream;
size_t write(uint8_t c);
// for indentation
uint8_t _indent;
uint8_t _indentStep;
uint8_t _indent;
uint8_t _indentStep;
// configuration
uint8_t _config;
// stack - used to remember the current tagname to create
// automatic the right close tag.
uint8_t _idx;
char tagStack[XMLWRITER_MAXLEVEL][XMLWRITER_MAXTAGSIZE+1];
uint8_t _tidx;
char _tagStack[XMLWRITER_MAXLEVEL][XMLWRITER_MAXTAGSIZE + 1];
char * _buffer;
uint8_t _bufsize;
uint8_t _bidx;
uint32_t _bytesOut;
};
#endif
// END OF FILE
// -- END OF FILE --

View File

@ -1,12 +1,10 @@
//
// FILE: KMLWriterTest.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// VERSION: 0.1.3
// PURPOSE: simple KML writer
// DATE: 2015-05-21
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/XMLWriter
//
#include <XMLWriter.h>
@ -19,7 +17,11 @@ void setup()
{
Serial.begin(115200);
uint32_t start = micros();
KMLTest();
uint32_t stop = micros();
Serial.println();
Serial.println(stop - start);
}
void loop()
@ -86,3 +88,6 @@ This is a demo of the XMLWriter lib for Arduino
</Document>
</kml>
*/
// -- END OF FILE --

View File

@ -0,0 +1,122 @@
//
// FILE: XMLWriterDefaultSerial.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.1
// PURPOSE: make a simple XML generating lib
// DATE: 2020-04-24
// URL: https://github.com/RobTillaart/XMLWriter
//
#include <XMLWriter.h>
XMLWriter XML;
char buffer[24];
void setup()
{
Serial.begin(115200);
uint32_t start = micros();
XML.header();
XML.comment("XMLWriterTest.ino\nThis is a demo of a simple XML lib for Arduino", true);
XML.tagOpen("Arduino", "42");
XML.tagOpen("Ports");
AnalogPorts("before");
DigitalPorts();
AnalogPorts("after");
XML.tagClose();
Weather();
Weather2();
DataTypes();
XML.tagClose();
uint32_t stop = micros();
Serial.println();
Serial.println(stop - start);
}
void Weather2()
{
XML.comment("The weather in South Africa");
for (int i = 0; i < 10; i++)
{
XML.tagStart("Weather");
XML.tagField("Date", "20131106");
XML.tagField("Time", "1:42");
XML.tagField("Temp", "23.4");
XML.tagField("Humi", "50%");
XML.tagField("Rain", "10mm");
XML.tagField("Sun", "40");
XML.tagEnd();
}
}
void Weather()
{
XML.comment("The weather in Nebraska");
XML.tagOpen("Weather");
XML.writeNode("Date", "20131106");
XML.writeNode("Time", "11:42");
XML.writeNode("Temp", "23.4");
XML.writeNode("Humi", "50%");
XML.writeNode("Rain", "10mm");
XML.writeNode("Sun", "40");
XML.tagClose();
}
void AnalogPorts(const char* name)
{
XML.comment("The analog ports are multiplexed");
XML.tagOpen("Analog", name);
XML.writeNode("Analog0", itoa(analogRead(A0), buffer, 10));
XML.writeNode("Analog1", analogRead(A1));
XML.writeNode("Analog2", (5.0 * analogRead(A2)) / 1023); // default nr decimals = 2
XML.writeNode("Analog3", (5.0 * analogRead(A2)) / 1023, 3);
XML.tagClose();
}
void DigitalPorts()
{
XML.comment("The digital ports are not multiplexed");
XML.tagOpen("Digital");
XML.writeNode("D1", itoa(digitalRead(1), buffer, 10));
XML.writeNode("D13", digitalRead(13));
XML.tagClose();
}
void DataTypes()
{
XML.comment("Testing dataTypes I");
XML.tagOpen("Datatypes");
XML.writeNode("Bool", 1 == 1);
XML.writeNode("Bool", 1 == 0);
XML.writeNode("BIN", 42, BIN);
XML.writeNode("DEC", 42, DEC);
XML.writeNode("HEX", 42, HEX);
XML.writeNode("OCT", 42, OCT);
XML.tagClose();
XML.comment("Testing dataTypes II");
for (int i = 0; i < 3; i++)
{
XML.tagStart("dataTypes");
XML.tagField("Bool", 1 == 1);
XML.tagField("Bool", 1 == 0);
int x = analogRead(A0);
XML.tagField("BIN", x, BIN);
XML.tagField("DEC", x, DEC);
XML.tagField("HEX", x, HEX);
XML.tagField("OCT", x, OCT);
XML.tagEnd();
}
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
XMLWriterTest.ino
This is a demo of a simple XML lib for Arduino
-->
<Arduino name="42">
<Ports>
<!-- The analog ports are multiplexed -->
<Analog name="before">
<Analog0>389</Analog0>
<Analog1>332</Analog1>
<Analog2>1.55</Analog2>
<Analog3>1.544</Analog3>
</Analog>
<!-- The digital ports are not multiplexed -->
<Digital>
<D1>0</D1>
<D13>1</D13>
</Digital>
<!-- The analog ports are multiplexed -->
<Analog name="after">
<Analog0>343</Analog0>
<Analog1>366</Analog1>
<Analog2>1.94</Analog2>
<Analog3>1.926</Analog3>
</Analog>
</Ports>
<!-- The weather in Nebraska -->
<Weather>
<Date>20131106</Date>
<Time>11:42</Time>
<Temp>23.4</Temp>
<Humi>50%</Humi>
<Rain>10mm</Rain>
<Sun>40</Sun>
</Weather>
<!-- The weather in South Africa -->
<Weather Date="20131106" Time="1:42" Temp="23.4" Humi="50%" Rain="10mm" Sun="40"/>
<Weather Date="20131106" Time="1:42" Temp="23.4" Humi="50%" Rain="10mm" Sun="40"/>
<Weather Date="20131106" Time="1:42" Temp="23.4" Humi="50%" Rain="10mm" Sun="40"/>
<Weather Date="20131106" Time="1:42" Temp="23.4" Humi="50%" Rain="10mm" Sun="40"/>
<Weather Date="20131106" Time="1:42" Temp="23.4" Humi="50%" Rain="10mm" Sun="40"/>
<Weather Date="20131106" Time="1:42" Temp="23.4" Humi="50%" Rain="10mm" Sun="40"/>
<Weather Date="20131106" Time="1:42" Temp="23.4" Humi="50%" Rain="10mm" Sun="40"/>
<Weather Date="20131106" Time="1:42" Temp="23.4" Humi="50%" Rain="10mm" Sun="40"/>
<Weather Date="20131106" Time="1:42" Temp="23.4" Humi="50%" Rain="10mm" Sun="40"/>
<Weather Date="20131106" Time="1:42" Temp="23.4" Humi="50%" Rain="10mm" Sun="40"/>
<!-- Testing dataTypes I -->
<Datatypes>
<Bool>true</Bool>
<Bool>false</Bool>
<BIN>101010</BIN>
<DEC>42</DEC>
<HEX>2A</HEX>
<OCT>52</OCT>
</Datatypes>
<!-- Testing dataTypes II -->
<dataTypes Bool="true" Bool="false" BIN="101110111" DEC="375" HEX="177" OCT="567"/>
<dataTypes Bool="true" Bool="false" BIN="101010010" DEC="338" HEX="152" OCT="522"/>
<dataTypes Bool="true" Bool="false" BIN="101111010" DEC="378" HEX="17A" OCT="572"/>
</Arduino>

View File

@ -0,0 +1,271 @@
//
// FILE: XMLWriterEthernet.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo XML writer for EthernetClient
// DATE: 2020-04-24
// URL:
//
#include <XMLWriter.h>
#include <SPI.h>
#include <Ethernet.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip( 192, 168, 1, 177);
const int EthernetPIN = 10;
EthernetServer server(80); // change to your config
char httpRequest[40];
uint8_t reqCnt;
////////////////////////////////////////////////////////////////////
//
// HTTP HELPER CODE
//
void HTTP_header(EthernetClient* cl, const char *contentType, bool keepAlive = false, int refresh = 5)
{
cl->println("HTTP/1.1 200 OK");
cl->print("Content-Type: ");
cl->println( contentType );
cl->println("Connection: ");
cl->println(keepAlive ? "keep-alive":"close");
cl->println("Refresh: ");
cl->println(refresh);
cl->println();
}
////////////////////////////////////////////////////////////////////
//
// Based upon webServer example demo
//
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
// Ethernet.init(pin) to configure the CS pin
Ethernet.init(EthernetPIN);
Ethernet.begin(mac, ip);
if (Ethernet.hardwareStatus() == EthernetNoHardware)
{
Serial.println("No hardware found");
while (1);
}
if (Ethernet.linkStatus() == LinkOFF)
{
Serial.println("Cable is not connected.");
Serial.println("Connect cable");
}
while (Ethernet.linkStatus() == LinkOFF); // wait for cable
server.begin();
Serial.print("ServerIP: ");
Serial.println(Ethernet.localIP());
}
void loop()
{
// listen for incoming clients
EthernetClient client = server.available();
if (client)
{
Serial.print("\n<CONNECTION>\n ");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
reqCnt = 0;
while (client.connected())
{
if (client.available())
{
char c = client.read();
Serial.write(c); // collect HHTP request here..
if (reqCnt < 39)
{
httpRequest[reqCnt++] = c;
httpRequest[reqCnt] = 0;
}
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank)
{
if (strstr(httpRequest, "1.xml"))
{
// send a standard http response header
HTTP_header(&client, "text/xml", true, 5);
XMLWriter XML(&client);
// XML body
XML.header();
XML.comment("XMLWriterTest.ino\nThis is a demo of a simple XML lib for Arduino", true);
// use of {} to get indentation that follows the XML (sort of)
// it adds no code size, but improves readability a lot
XML.tagOpen("Arduino", "42");
{
XML.tagOpen("Ports");
{
AnalogPorts(XML, "before");
DigitalPorts(XML);
AnalogPorts(XML,"after");
}
XML.tagClose();
}
XML.tagClose();
break;
}
if (strstr(httpRequest, "2.xml"))
{
// send a standard http response header
HTTP_header(&client, "text/xml", true, 5);
XMLWriter XML(&client);
// XML body
XML.header();
XML.comment("XMLWriterTest.ino\nThis is a demo of a simple XML lib for Arduino", true);
// use of {} to get indentation that follows the XML (sort of)
// it adds no code size, but improves readability a lot
XML.tagOpen("Arduino", "102");
{
Weather(XML);
Weather2(XML);
DataTypes(XML);
}
XML.tagClose();
break;
}
// default page is simple HTML
// send a standard http response header
HTTP_header(&client, "text/html", true, 5);
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.println("<head>");
client.println("<title>Demo XML writer</title>");
client.println("</head>");
client.println("<body>");
client.println("<h1>Demo XML writer for EthernetClient</h1>");
client.println("<p>Get <a href=\"1.xml\">XML 1</a>.</p>");
client.println("<p>Get <a href=\"2.xml\">XML 2</a>.</p>");
client.println("</body>");
client.println("</html>");
break;
}
if (c == '\n') {
Serial.print(" ");
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
client.stop();
Serial.println("</CONNECTION>");
}
}
////////////////////////////////////////////////////////////////////
//
// XML CODE
//
void Weather2(XMLWriter xw)
{
xw.comment("The weather in South Africa");
for (int i = 0; i < 10; i++)
{
xw.tagStart("Weather");
xw.tagField("Date", "20131106");
xw.tagField("Time", "1:42");
xw.tagField("Temp", "23.4");
xw.tagField("Humi", "50%");
xw.tagField("Rain", "10mm");
xw.tagField("Sun", "40");
xw.tagEnd();
}
}
void Weather(XMLWriter xw)
{
xw.comment("The weather in Nebraska");
xw.tagOpen("Weather");
xw.writeNode("Date", "20131106");
xw.writeNode("Time", "11:42");
xw.writeNode("Temp", "23.4");
xw.writeNode("Humi", "50%");
xw.writeNode("Rain", "10mm");
xw.writeNode("Sun", "40");
xw.tagClose();
}
void AnalogPorts(XMLWriter xw, const char* name)
{
char buffer[24];
xw.comment("The analog ports are multiplexed");
xw.tagOpen("Analog", name);
xw.writeNode("Analog0", itoa(analogRead(A0), buffer, 10));
xw.writeNode("Analog1", analogRead(A1));
xw.writeNode("Analog2", (5.0 * analogRead(A2)) / 1023); // default nr decimals = 2
xw.writeNode("Analog3", (5.0 * analogRead(A2)) / 1023, 3);
xw.tagClose();
}
void DigitalPorts(XMLWriter xw)
{
char buffer[24];
xw.comment("The digital ports are not multiplexed");
xw.tagOpen("Digital");
xw.writeNode("D1", itoa(digitalRead(1), buffer, 10));
xw.writeNode("D13", digitalRead(13));
xw.tagClose();
}
void DataTypes(XMLWriter xw)
{
xw.comment("Testing dataTypes I");
xw.tagOpen("Datatypes");
xw.writeNode("BoolT", 1 == 1);
xw.writeNode("BoolF", 1 == 0);
xw.writeNode("BIN", 42, BIN);
xw.writeNode("DEC", 42, DEC);
xw.writeNode("HEX", 42, HEX);
xw.writeNode("OCT", 42, OCT);
xw.tagClose();
xw.comment("Testing dataTypes II");
for (int i = 0; i < 3; i++)
{
xw.tagStart("dataTypes");
xw.tagField("BoolT", 1 == 1);
xw.tagField("BoolF", 1 == 0);
int x = analogRead(A0);
xw.tagField("BIN", x, BIN);
xw.tagField("DEC", x, DEC);
xw.tagField("HEX", x, HEX);
xw.tagField("OCT", x, OCT);
xw.tagEnd();
}
}
// -- END OF FILE --

View File

@ -0,0 +1,289 @@
//
// FILE: XMLWriterEthernet.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.1
// PURPOSE: demo XML writer for EthernetClient
// DATE: 2020-04-24
// URL: https://github.com/RobTillaart/XMLWriter
//
#include <XMLWriter.h>
#include <SPI.h>
#include <Ethernet.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip( 192, 168, 1, 177);
const int EthernetPIN = 10;
EthernetServer server(80); // change to your config
char httpRequest[40];
uint8_t reqCnt;
////////////////////////////////////////////////////////////////////
//
// HTTP HELPER CODE
//
void HTTP_header(EthernetClient* cl, const char *contentType, bool keepAlive = false, int refresh = 5)
{
cl->println("HTTP/1.1 200 OK");
cl->print("Content-Type: ");
cl->println( contentType );
cl->println("Connection: ");
cl->println(keepAlive ? "keep-alive" : "close");
cl->println("Refresh: ");
cl->println(refresh);
cl->println();
}
////////////////////////////////////////////////////////////////////
//
// Based upon webServer example demo
//
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
// Ethernet.init(pin) to configure the CS pin
Ethernet.init(EthernetPIN);
Ethernet.begin(mac, ip);
if (Ethernet.hardwareStatus() == EthernetNoHardware)
{
Serial.println("No hardware found");
while (1);
}
Serial.println(Ethernet.hardwareStatus());
Serial.println(Ethernet.linkStatus());
if (Ethernet.linkStatus() == LinkOFF)
{
Serial.println("Cable is not connected.");
Serial.println("Connect cable");
}
while (Ethernet.linkStatus() == LinkOFF); // wait for cable
server.begin();
Serial.print("ServerIP: ");
Serial.println(Ethernet.localIP());
}
void loop()
{
// listen for incoming clients
EthernetClient client = server.available();
if (client)
{
Serial.print("\n<CONNECTION>\n ");
// Serial.println(client.remoteIP());
// Serial.println(client.remotePort());
// an http request ends with a blank line
boolean currentLineIsBlank = true;
reqCnt = 0;
while (client.connected())
{
if (client.available())
{
char c = client.read();
Serial.write(c); // collect HHTP request here..
if (reqCnt < 39)
{
httpRequest[reqCnt++] = c;
httpRequest[reqCnt] = 0;
}
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank)
{
if (strstr(httpRequest, "1.xml"))
{
uint32_t start = micros();
// send a standard http response header
HTTP_header(&client, "text/xml", true, 5);
XMLWriter XML(&client, 250);
// XML body
XML.header();
XML.comment("XMLWriterTest.ino\nThis is a demo of a simple XML lib for Arduino", true);
// use of {} to get indentation that follows the XML (sort of)
// it adds no code size, but improves readability a lot
XML.tagOpen("Arduino", "42");
{
XML.tagOpen("Ports");
{
AnalogPorts(&XML, "before");
DigitalPorts(&XML);
AnalogPorts(&XML, "after");
}
XML.tagClose();
}
XML.tagClose();
XML.flush();
uint32_t stop = micros();
Serial.println(stop - start);
Serial.println(XML.bytesWritten());
Serial.println(XML.bytesWritten() * 1e6 / (stop - start));
break;
}
if (strstr(httpRequest, "2.xml"))
{
uint32_t start = micros();
// send a standard http response header
HTTP_header(&client, "text/xml", true, 5);
XMLWriter XML(&client, 250);
// XML body
XML.header();
XML.comment("XMLWriterTest.ino\nThis is a demo of a simple XML lib for Arduino", true);
// use of {} to get indentation that follows the XML (sort of)
// it adds no code size, but improves readability a lot
XML.tagOpen("Arduino", "102");
{
Weather(&XML);
Weather2(&XML);
DataTypes(&XML);
}
XML.tagClose();
XML.flush();
uint32_t stop = micros();
Serial.println(stop - start);
Serial.println(XML.bytesWritten());
Serial.println(XML.bytesWritten() * 1e6 / (stop - start));
break;
}
// default page is simple HTML
// send a standard http response header
HTTP_header(&client, "text/html", true, 5);
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.println("<head>");
client.println("<title>Demo XML writer</title>");
client.println("</head>");
client.println("<body>");
client.println("<h1>Demo XML writer for EthernetClient</h1>");
client.println("<p>Get <a href=\"1.xml\">XML 1</a>.</p>");
client.println("<p>Get <a href=\"2.xml\">XML 2</a>.</p>");
client.println("</body>");
client.println("</html>");
break;
}
if (c == '\n') {
Serial.print(" ");
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
client.stop();
Serial.println("</CONNECTION>");
}
}
////////////////////////////////////////////////////////////////////
//
// XML CODE
//
void Weather2(XMLWriter* xw)
{
xw->comment("The weather in South Africa");
for (int i = 0; i < 10; i++)
{
xw->tagStart("Weather");
xw->tagField("Date", "20131106");
xw->tagField("Time", "1:42");
xw->tagField("Temp", "23.4");
xw->tagField("Humi", "50%");
xw->tagField("Rain", "10mm");
xw->tagField("Sun", "40");
xw->tagEnd();
}
}
void Weather(XMLWriter* xw)
{
xw->comment("The weather in Nebraska");
xw->tagOpen("Weather");
xw->writeNode("Date", "20131106");
xw->writeNode("Time", "11:42");
xw->writeNode("Temp", "23.4");
xw->writeNode("Humi", "50%");
xw->writeNode("Rain", "10mm");
xw->writeNode("Sun", "40");
xw->tagClose();
}
void AnalogPorts(XMLWriter* xw, const char* name)
{
char buffer[24];
xw->comment("The analog ports are multiplexed");
xw->tagOpen("Analog", name);
xw->writeNode("Analog0", itoa(analogRead(A0), buffer, 10));
xw->writeNode("Analog1", analogRead(A1));
xw->writeNode("Analog2", (5.0 * analogRead(A2)) / 1023); // default nr decimals = 2
xw->writeNode("Analog3", (5.0 * analogRead(A2)) / 1023, 3);
xw->tagClose();
}
void DigitalPorts(XMLWriter* xw)
{
char buffer[24];
xw->comment("The digital ports are not multiplexed");
xw->tagOpen("Digital");
xw->writeNode("D1", itoa(digitalRead(1), buffer, 10));
xw->writeNode("D13", digitalRead(13));
xw->tagClose();
}
void DataTypes(XMLWriter* xw)
{
xw->comment("Testing dataTypes I");
xw->tagOpen("Datatypes");
xw->writeNode("BoolT", 1 == 1);
xw->writeNode("BoolF", 1 == 0);
xw->writeNode("BIN", 42, BIN);
xw->writeNode("DEC", 42, DEC);
xw->writeNode("HEX", 42, HEX);
xw->writeNode("OCT", 42, OCT);
xw->tagClose();
xw->comment("Testing dataTypes II");
for (int i = 0; i < 3; i++)
{
xw->tagStart("dataTypes");
xw->tagField("BoolT", 1 == 1);
xw->tagField("BoolF", 1 == 0);
int x = analogRead(A0);
xw->tagField("BIN", x, BIN);
xw->tagField("DEC", x, DEC);
xw->tagField("HEX", x, HEX);
xw->tagField("OCT", x, OCT);
xw->tagEnd();
}
}
// -- END OF FILE --

View File

@ -0,0 +1,96 @@
//
// FILE: XMLWriterSDcard.ino.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.1
// PURPOSE: XML writing to SD card
// DATE: 2020-04-24
// URL: https://github.com/RobTillaart/XMLWriter
//
// Note: this application will write to the SD card immediately
// and it will append to the data.xml file every time started.
//
#include <SPI.h>
#include <SD.h>
// SPI PINS
// MOSI 11
// MISO 12
// CLOCK 13
// CS 10
#define CS 10 // adjust this ChipSelect line if needed !
#include <XMLWriter.h>
char buffer[24];
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
// initialize the SD card
if (!SD.begin(CS))
{
Serial.println("Error: SD card failure");
while(1);
}
// remove file for proper timing
SD.remove("data.xml");
delay(1000);
uint32_t start = micros();
File logfile = SD.open("data.xml", FILE_WRITE);
if (!logfile)
{
Serial.println("Error: SD card failure");
while(1);
}
XMLWriter XML(&logfile);
XML.header();
XML.comment("XMLWriterSDcard.ino\nThis is a demo of a simple XML lib for Arduino", true);
XML.tagOpen("Arduino", "42");
XML.tagOpen("Ports");
AnalogPorts(&XML, "measurement");
DigitalPorts(&XML);
XML.tagClose();
XML.tagClose();
XML.flush();
logfile.close();
uint32_t stop = micros();
Serial.println();
Serial.println(stop - start);
Serial.println("Done...");
}
void AnalogPorts(XMLWriter* xw, const char* name)
{
xw->comment("The analog ports are multiplexed");
xw->tagOpen("Analog", name);
xw->writeNode("Analog0", itoa(analogRead(A0), buffer, 10));
xw->writeNode("Analog1", analogRead(A1));
xw->writeNode("Analog2", (5.0*analogRead(A2))/1023); // default nr decimals = 2
xw->writeNode("Analog3", (5.0*analogRead(A2))/1023, 3);
xw->tagClose();
}
void DigitalPorts(XMLWriter* xw)
{
xw->comment("The digital ports are not multiplexed");
xw->tagOpen("Digital");
xw->writeNode("D1", itoa(digitalRead(1), buffer, 10));
xw->writeNode("D13", digitalRead(13));
xw->tagClose();
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,3 @@
https://learn.adafruit.com/adafruit-data-logger-shield/using-the-sd-card

View File

@ -0,0 +1,6 @@
# XMLWriter
### SDCARD demo notes
Do not forget the **XML.flush();** before closing the FILE object.

View File

@ -1,12 +1,10 @@
//
// FILE: XMLWriterTest.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.03
// VERSION: 0.1.5
// PURPOSE: make a simple XML generating lib
// DATE: 2013-11-06
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/XMLWriter
//
#include <XMLWriter.h>
@ -19,9 +17,12 @@ void setup()
{
Serial.begin(115200);
uint32_t start = micros();
XML.setConfig(0); // comment this line to see difference
XML.header();
XML.comment("XMLWriterTest.ino\nThis is a demo of a simple XML lib for Arduino", true);
// XML.newLine(0);
XML.tagOpen("Arduino", "42");
XML.tagOpen("Ports");
@ -35,12 +36,15 @@ void setup()
DataTypes();
XML.tagClose();
uint32_t stop = micros();
Serial.println(stop - start);
Serial.println("done...");
}
void Weather2()
{
XML.comment("The weather in South Africa");
for (int i=0; i<10; i++)
for (int i = 0; i < 10; i++)
{
XML.tagStart("Weather");
XML.tagField("Date", "20131106");
@ -72,8 +76,8 @@ void AnalogPorts(const char* name)
XML.tagOpen("Analog", name);
XML.writeNode("Analog0", itoa(analogRead(A0), buffer, 10));
XML.writeNode("Analog1", analogRead(A1));
XML.writeNode("Analog2", (5.0*analogRead(A2))/1023); // default nr decimals = 2
XML.writeNode("Analog3", (5.0*analogRead(A2))/1023, 3);
XML.writeNode("Analog2", (5.0 * analogRead(A2)) / 1023); // default nr decimals = 2
XML.writeNode("Analog3", (5.0 * analogRead(A2)) / 1023, 3);
XML.tagClose();
}
@ -90,20 +94,20 @@ void DataTypes()
{
XML.comment("Testing dataTypes I");
XML.tagOpen("Datatypes");
XML.writeNode("Bool", 1 == 1);
XML.writeNode("Bool", 1 == 0);
XML.writeNode("BoolT", 1 == 1);
XML.writeNode("BoolF", 1 == 0);
XML.writeNode("BIN", 42, BIN);
XML.writeNode("DEC", 42, DEC);
XML.writeNode("HEX", 42, HEX);
XML.writeNode("OCT", 42, OCT);
XML.tagClose();
XML.comment("Testing dataTypes II");
for (int i=0; i<3; i++)
for (int i = 0; i < 3; i++)
{
XML.tagStart("dataTypes");
XML.tagField("Bool", 1 == 1);
XML.tagField("Bool", 1 == 0);
XML.tagField("BoolT", 1 == 1);
XML.tagField("BoolF", 1 == 0);
int x = analogRead(A0);
XML.tagField("BIN", x, BIN);
XML.tagField("DEC", x, DEC);
@ -115,4 +119,6 @@ void DataTypes()
void loop()
{
}
}
// -- END OF FILE --

View File

@ -1,12 +1,10 @@
//
// FILE: XMLWriterDemo01.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// VERSION: 0.1.2
// PURPOSE: XML writer demo
// DATE: 2016-03-16
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/XMLWriter
//
#include <XMLWriter.h>
@ -21,7 +19,11 @@ void setup()
float y = random(100) * 1.0;
float r = random(100) * 1.0;
uint32_t start = micros();
shoot(p, y, r);
uint32_t stop = micros();
Serial.println();
Serial.println(stop - start);
}
void shoot(float p, float y, float r)
@ -80,9 +82,20 @@ void shoot(float p, float y, float r)
XML.tagStart("/position");
XML.tagEnd(NEWLINE, NOSLASH);
XML.comment("test escape() codes");
XML.tagOpen("supported");
XML.writeNode("ampersand", "&");
XML.writeNode("lessthan", "<");
XML.writeNode("greaterthan", ">");
XML.writeNode("quote", "\"");
XML.writeNode("apostrophe", "\'");
XML.tagClose();
XML.tagClose(); // shoot
}
void loop()
{
}
// -- END OF FILE --

View File

@ -1,17 +1,9 @@
#######################################
# Syntax Coloring Map For XMLWriter
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
XMLWriter KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
reset KEYWORD2
header KEYWORD2
comment KEYWORD2
@ -27,16 +19,12 @@ decrIndent KEYWORD2
indent KEYWORD2
raw KEYWORD2
escape KEYWORD2
setConfig KEYWORD2
newLine KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
#######################################
# Constants (LITERAL1)
#######################################
NOMULTILINE LITERAL1
MULTILINE LITERAL1
NEWLINE LITERAL1
@ -44,3 +32,6 @@ NONEWLINE LITERAL1
NOINDENT LITERAL1
SLASH LITERAL1
NOSLASH LITERAL1
XMLWRITER_NONE LITERAL1
XMLWRITER_COMMENT LITERAL1
XMLWRITER_INDENT LITERAL1

View File

@ -1,7 +1,7 @@
{
"name": "XMLWriter",
"keywords": "Write,XML,node,header,tag,indent,field,stream",
"description": "Library for writing XML files.",
"keywords": "Write, XML, node, header, tag, indent, field, stream,",
"description": "Arduino library for creating XML",
"authors":
[
{
@ -13,12 +13,12 @@
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/Arduino.git"
"url": "https://github.com/RobTillaart/XMLWriter"
},
"version":"0.1.8",
"version":"0.2.2",
"frameworks": "arduino",
"platforms": "*",
"export": {
"include": "libraries/XMLWriter"
"include": "XMLWriter"
}
}

View File

@ -1,10 +1,11 @@
name=XMLWriter
version=0.1.8
version=0.2.2
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Library for writing XML files.
sentence=Arduino library for creating XML
paragraph=
category=Data Processing
url=https://github.com/RobTillaart/Arduino/tree/master/libraries
url=https://github.com/RobTillaart/XMLWriter
architectures=*
includes=XMLWriter.h
depends=