xpl lib

Dependents:   XPL-App4_cleanup XPL-App5

Files at this revision

API Documentation at this revision

Comitter:
richnash
Date:
Tue Oct 09 17:37:05 2018 +0000
Commit message:
ready to move to cli to explore D11 pin fix

Changed in this revision

xPL.cpp Show annotated file Show diff for this revision Revisions of this file
xPL.h Show annotated file Show diff for this revision Revisions of this file
xPL_Message.cpp Show annotated file Show diff for this revision Revisions of this file
xPL_Message.h Show annotated file Show diff for this revision Revisions of this file
xPL_utils.cpp Show annotated file Show diff for this revision Revisions of this file
xPL_utils.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xPL.cpp	Tue Oct 09 17:37:05 2018 +0000
@@ -0,0 +1,402 @@
+/*
+ * xPL.Arduino v0.1, xPL Implementation for Arduino
+ *
+ * This code is parsing a xPL message stored in 'received' buffer
+ * - isolate and store in 'line' buffer each part of the message -> detection of EOL character (DEC 10)
+ * - analyse 'line', function of its number and store information in xpl_header memory
+ * - check for each step if the message respect xPL protocol
+ * - parse each command line
+ *
+ * Copyright (C) 2012 johan@pirlouit.ch, olivier.lebrun@gmail.com
+ * Original version by Gromain59@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+ 
+#include "xPL.h"
+
+#define XPL_LINE_MESSAGE_BUFFER_MAX         128 // max length of a line         // maximum command in a xpl message
+#define XPL_END_OF_LINE                     10
+
+// define the line number identifier
+#define XPL_MESSAGE_TYPE_IDENTIFIER         1
+#define XPL_OPEN_HEADER                     2
+#define XPL_HOP_COUNT                       3
+#define XPL_SOURCE                          4
+#define XPL_TARGET                          5
+#define XPL_CLOSE_HEADER                    6
+#define XPL_SCHEMA_IDENTIFIER               7
+#define XPL_OPEN_SCHEMA                     8
+
+// Heartbeat request class definition
+//prog_char XPL_HBEAT_REQUEST_CLASS_ID[] PROGMEM = "hbeat";
+//prog_char XPL_HBEAT_REQUEST_TYPE_ID[] PROGMEM = "request";
+//prog_char XPL_HBEAT_ANSWER_CLASS_ID[] PROGMEM = "hbeat";
+//prog_char XPL_HBEAT_ANSWER_TYPE_ID[] PROGMEM = "basic";  //app, basic
+#define XPL_HBEAT_REQUEST_CLASS_ID  "hbeat"
+#define XPL_HBEAT_REQUEST_TYPE_ID  "request"
+#define XPL_HBEAT_ANSWER_CLASS_ID  "hbeat"
+#define XPL_HBEAT_ANSWER_TYPE_ID  "app"
+
+/* xPL Class */
+xPL::xPL()
+{
+  udp_port = XPL_UDP_PORT;
+  
+  SendExternal = NULL;
+
+#ifdef ENABLE_PARSING
+  AfterParseAction = NULL;
+
+  last_heartbeat = 0;
+  hbeat_interval = XPL_DEFAULT_HEARTBEAT_INTERVAL;
+  xpl_accepted = XPL_ACCEPT_ALL;
+#endif
+}
+
+xPL::~xPL()
+{
+}
+
+/// Set the source of outgoing xPL messages
+void xPL::SetSource(const char * _vendorId, const char * _deviceId, const char * _instanceId)
+{
+    memcpy(source.vendor_id, _vendorId, XPL_VENDOR_ID_MAX);
+    memcpy(source.device_id, _deviceId, XPL_DEVICE_ID_MAX);
+    memcpy(source.instance_id, _instanceId, XPL_INSTANCE_ID_MAX);
+}
+
+/**
+ * \brief       Send an xPL message
+ * \details   There is no validation of the message, it is sent as is.
+ * \param    buffer         buffer containing the xPL message.
+ */
+void xPL::SendMessage(char *_buffer)
+{
+    (*SendExternal)(_buffer);
+}
+
+/**
+ * \brief       Send an xPL message
+ * \details   There is no validation of the message, it is sent as is.
+ * \param    message                    An xPL message.
+ * \param    _useDefaultSource  if true, insert the default source (defined in SetSource) on the message.
+ */
+void xPL::SendMessage(xPL_Message *_message, bool _useDefaultSource)
+{
+    if(_useDefaultSource)
+    {
+        _message->SetSource(source.vendor_id, source.device_id, source.instance_id);
+    }
+
+    SendMessage(_message->toString());
+}
+
+#ifdef ENABLE_PARSING
+
+/**
+ * \brief       xPL Stuff
+ * \details   Send heartbeat messages at "hbeat_interval" interval
+ */
+void xPL::Process()
+{
+    static bool bFirstRun = true;
+
+    // Check heartbeat + send
+    //if ((millis()-last_heartbeat >= (unsigned long)hbeat_interval * 1000)
+    //      || (bFirstRun && millis() > 3000))
+    if ((clock()-last_heartbeat >= (unsigned long)hbeat_interval * 1000)
+          || (bFirstRun && clock() > 3000))
+    {
+        SendHBeat();
+        bFirstRun = false;
+    }
+}
+
+/**
+ * \brief       Parse an ingoing xPL message
+ * \details   Parse a message, check for hearbeat request and call user defined callback for post processing.
+ * \param    buffer         buffer of the ingoing UDP Packet
+ */
+void xPL::ParseInputMessage(char* _buffer)
+{
+    xPL_Message* xPLMessage = new xPL_Message();
+    Parse(xPLMessage, _buffer);
+    
+    // check if the message is an hbeat.request to send a heartbeat
+    if (CheckHBeatRequest(xPLMessage))
+    {
+        SendHBeat();
+    }
+    
+    // call the user defined callback to execute an action
+    if(AfterParseAction != NULL)
+    {
+      (*AfterParseAction)(xPLMessage);
+    }
+    
+    delete xPLMessage;
+}
+
+/**
+ * \brief       Check the xPL message target
+ * \details   Check if the xPL message is for us
+ * \param    _message         an xPL message
+ */
+bool xPL::TargetIsMe(xPL_Message * _message)
+{
+  if (memcmp(_message->target.vendor_id, source.vendor_id, strlen(source.vendor_id)) != 0)
+    return false;
+
+  if (memcmp(_message->target.device_id, source.device_id, strlen(source.device_id)) != 0)
+    return false;
+
+  if (memcmp(_message->target.instance_id, source.instance_id, strlen(source.instance_id)) != 0)
+    return false;
+
+  return true;
+}
+
+/**
+ * \brief       Send a heartbeat message
+  */
+void xPL::SendHBeat()
+{
+    last_heartbeat = clock(); //millis();
+    char buffer[XPL_MESSAGE_BUFFER_MAX];
+    
+//  sprintf_P(buffer, PSTR("xpl-stat\n{\nhop=1\nsource=%s-%s.%s\ntarget=*\n}\n%s.%s\n{\ninterval=%d\n}\n"), source.vendor_id, source.device_id, source.instance_id, XPL_HBEAT_ANSWER_CLASS_ID, XPL_HBEAT_ANSWER_TYPE_ID, hbeat_interval);
+    //sprintf(buffer, "xpl-stat\r\n{\r\nhop=1\r\nsource=%s-%s.%s\r\ntarget=*\r\n}\r\n%s.%s\r\n{\r\ninterval=%d\r\nport=3865\r\nremote-ip=8.8.8.8\r\nversion=1.0\r\n}\r\n", source.vendor_id, source.device_id, source.instance_id, XPL_HBEAT_ANSWER_CLASS_ID, XPL_HBEAT_ANSWER_TYPE_ID, hbeat_interval);
+    sprintf(buffer, "xpl-stat\n{\nhop=1\nsource=%s-%s.%s\ntarget=*\n}\n%s.%s\n{\ninterval=%d\nport=3865\nremote-ip=8.8.8.8\nversion=1.0\n}\n", source.vendor_id, source.device_id, source.instance_id, XPL_HBEAT_ANSWER_CLASS_ID, XPL_HBEAT_ANSWER_TYPE_ID, hbeat_interval);
+    
+    //(*SendExternal)(buffer);
+    SendMessage(buffer);
+    
+    printf("XPL: HB Sent\r\n");
+}
+
+/**
+ * \brief       Check if the message is a heartbeat request
+  * \param    _message         an xPL message
+ */
+inline bool xPL::CheckHBeatRequest(xPL_Message* _message)
+{
+  if (!TargetIsMe(_message))
+    return false;
+
+  return _message->IsSchema(XPL_HBEAT_REQUEST_CLASS_ID, XPL_HBEAT_REQUEST_TYPE_ID);
+}
+
+/**
+ * \brief       Parse a buffer and generate a xPL_Message
+ * \details   Line based xPL parser
+ * \param    _xPLMessage    the result xPL message
+ * \param    _message         the buffer
+ */
+void xPL::Parse(xPL_Message* _xPLMessage, char* _buffer)
+{
+    int len = strlen(_buffer);
+
+    short j=0;
+    short line=0;
+    int result=0;
+    char lineBuffer[XPL_LINE_MESSAGE_BUFFER_MAX+1];
+
+    // read each character of the message
+    for(short i = 0; i < len; i++)
+    {
+        // load byte by byte in 'line' buffer, until '\n' is detected
+        if(_buffer[i] == XPL_END_OF_LINE) // is it a linefeed (ASCII: 10 decimal)
+        {
+            ++line;
+            lineBuffer[j]='\0'; // add the end of string id
+            
+            if(line <= XPL_OPEN_SCHEMA)
+            {
+                // first part: header and schema determination
+                // we analyse the line, function of the line number in the xpl message
+                result = AnalyseHeaderLine(_xPLMessage, lineBuffer ,line);
+            }
+            
+            if(line > XPL_OPEN_SCHEMA)
+            {
+                // second part: command line
+                // we analyse the specific command line, function of the line number in the xpl message
+                result = AnalyseCommandLine(_xPLMessage, lineBuffer, line-9, j);
+
+                if(result == _xPLMessage->command_count+1)
+                    break;
+            }
+            
+            if (result < 0) break;
+
+            j = 0; // reset the buffer pointer
+            clearStr(lineBuffer); // clear the buffer
+        }
+        else
+        {
+            // next character
+            lineBuffer[j++] = _buffer[i];
+        }
+    }
+}
+
+/**
+ * \brief       Parse the header part of the xPL message line by line
+ * \param    _xPLMessage    the result xPL message
+ * \param    _buffer               the line to parse
+ * \param    _line                 the line number
+ */
+short xPL::AnalyseHeaderLine(xPL_Message* _xPLMessage, char* _buffer, short _line)
+{
+    switch (_line)
+    {
+        case XPL_MESSAGE_TYPE_IDENTIFIER: //message type identifier
+
+            if (memcmp(_buffer,"xpl-",4)==0) //xpl
+            {
+                if (memcmp(_buffer+4,"cmnd",4)==0) //command type
+                {
+                    _xPLMessage->type=XPL_CMND;  //xpl-cmnd
+                }
+                else if (memcmp(_buffer+4,"stat",4)==0) //statut type
+                {
+                    _xPLMessage->type=XPL_STAT;  //xpl-stat
+                }
+                else if (memcmp(_buffer+4,"trig",4)==0) // trigger type
+                {
+                    _xPLMessage->type=XPL_TRIG;  //xpl-trig
+                }
+            }
+            else
+            {
+                return 0;  //unknown message
+            }
+
+            return 1;
+
+            //break;
+
+        case XPL_OPEN_HEADER: //header begin
+
+            if (memcmp(_buffer,"{",1)==0)
+            {
+                return 2;
+            }
+            //else
+            //{
+                return -2;
+            //}
+
+            //break;
+
+        case XPL_HOP_COUNT: //hop
+            if (sscanf(_buffer, XPL_HOP_COUNT_PARSER, &_xPLMessage->hop))
+            {
+                return 3;
+            }
+            //else
+            //{
+                return -3;
+            //}
+
+            //break;
+
+        case XPL_SOURCE: //source
+            if (sscanf(_buffer, XPL_SOURCE_PARSER, &_xPLMessage->source.vendor_id, &_xPLMessage->source.device_id, &_xPLMessage->source.instance_id) == 3)
+            {
+              return 4;
+            }
+            //else
+            //{
+              return -4;
+            //}
+
+            //break;
+
+        case XPL_TARGET: //target
+
+            if (sscanf(_buffer, XPL_TARGET_PARSER, &_xPLMessage->target.vendor_id, &_xPLMessage->target.device_id, &_xPLMessage->target.instance_id) == 3)
+            {
+              return 5;
+            }
+            //else
+            //{
+              if(memcmp(_xPLMessage->target.vendor_id,"*", 1) == 0)  // check if broadcast message
+              {
+                  return 5;
+              }
+              //else
+              //{
+                  return -5;
+              //}
+            //}
+            //break;
+
+        case XPL_CLOSE_HEADER: //header end
+            if (memcmp(_buffer,"}",1)==0)
+            {
+                return 6;
+            }
+            //else
+            //{
+                return -6;
+            //}
+
+            //break;
+
+        case XPL_SCHEMA_IDENTIFIER: //schema
+            sscanf(_buffer, XPL_SCHEMA_PARSER, &_xPLMessage->schema.class_id, &_xPLMessage->schema.type_id);
+            return 7;
+        
+        case XPL_OPEN_SCHEMA: //header begin
+            if (memcmp(_buffer,"{",1)==0)
+            {
+                return 8;
+            }
+            //else
+            //{
+                return -8;
+            //}
+            //break;
+    }
+
+    return -100;
+}
+
+/**
+ * \brief       Parse the body part of the xPL message line by line
+ * \param    _xPLMessage                       the result xPL message
+ * \param    _buffer                               the line to parse
+ * \param    _command_line                 the line number
+ */
+short xPL::AnalyseCommandLine(xPL_Message * _xPLMessage, char *_buffer, short _command_line, short line_length)
+{
+    if (memcmp(_buffer,"}",1) == 0) // End of schema
+    {
+        return _xPLMessage->command_count+1;
+    }
+    else    // parse the next command
+    {
+        struct_command newcmd;
+        
+        sscanf(_buffer, XPL_COMMAND_PARSER, &newcmd.name, &newcmd.value);
+
+        _xPLMessage->AddCommand(newcmd.name, newcmd.value);
+
+        return _command_line;
+    }
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xPL.h	Tue Oct 09 17:37:05 2018 +0000
@@ -0,0 +1,104 @@
+/*
+ * xPL.Arduino v0.1, xPL Implementation for Arduino
+ *
+ * This code is parsing a xPL message stored in 'received' buffer
+ * - isolate and store in 'line' buffer each part of the message -> detection of EOL character (DEC 10)
+ * - analyse 'line', function of its number and store information in xpl_header memory
+ * - check for each step if the message respect xPL protocol
+ * - parse each command line
+ *
+ * Copyright (C) 2012 johan@pirlouit.ch, olivier.lebrun@gmail.com
+ * Original version by Gromain59@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#ifndef xPL_h
+#define xPL_h
+ 
+#define ENABLE_PARSING 1
+
+//#include "Arduino.h"
+#include "mbed.h"
+
+#include "xPL_utils.h"
+#include "xPL_Message.h"
+
+#define XPL_CMND 1
+#define XPL_STAT 2
+#define XPL_TRIG 3
+
+#define XPL_DEFAULT_HEARTBEAT_INTERVAL   300
+
+#define XPL_UDP_PORT 3865
+
+#define XPL_PORT_L  0x19
+#define XPL_PORT_H  0xF
+
+typedef enum {XPL_ACCEPT_ALL, XPL_ACCEPT_SELF, XPL_ACCEPT_SELF_ANY} xpl_accepted_type;
+// XPL_ACCEPT_ALL = all xpl messages
+// XPL_ACCEPT_SELF = only for me
+// XPL_ACCEPT_SELF_ANY = only for me and any (*)
+
+typedef void (*xPLSendExternal)(char*);
+typedef void (*xPLAfterParseAction)(xPL_Message * message);
+
+class xPL
+{
+  public:
+    xPL();
+    ~xPL();
+
+    struct_id source;  // my source
+    unsigned short udp_port;    // default 3865
+
+    xPLSendExternal SendExternal;
+
+    void SendMessage(char *);
+    void SendMessage(xPL_Message *, bool = true);
+
+    void SetSource(const char *,const char *,const char *);  // define my source
+
+#ifdef ENABLE_PARSING
+    xPLAfterParseAction AfterParseAction;
+
+
+    //byte hbeat_interval;  // default 5
+    short hbeat_interval;  // default 5
+    xpl_accepted_type xpl_accepted;
+
+
+    void Process();
+    void ParseInputMessage(char *buffer);
+
+    bool TargetIsMe(xPL_Message * message);
+
+    void SendHBeat();
+
+  private:
+    //void ClearData();
+    unsigned long last_heartbeat;
+//    void SendHBeat();
+    bool CheckHBeatRequest(xPL_Message * message);
+
+    void Parse(xPL_Message *, char *);
+    //byte AnalyseHeaderLine(xPL_Message *, char *, byte );
+    //byte AnalyseCommandLine(xPL_Message *, char *, byte, byte );
+    short AnalyseHeaderLine(xPL_Message *, char *, short );
+    short AnalyseCommandLine(xPL_Message *, char *, short, short );
+#endif
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xPL_Message.cpp	Tue Oct 09 17:37:05 2018 +0000
@@ -0,0 +1,215 @@
+/*
+ * xPL.Arduino v0.1, xPL Implementation for Arduino
+ *
+ * This code is parsing a xPL message stored in 'received' buffer
+ * - isolate and store in 'line' buffer each part of the message -> detection of EOL character (DEC 10)
+ * - analyse 'line', function of its number and store information in xpl_header memory
+ * - check for each step if the message respect xPL protocol
+ * - parse each command line
+ *
+ * Copyright (C) 2012 johan@pirlouit.ch, olivier.lebrun@gmail.com
+ * Original version by Gromain59@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+ 
+#include "xPL_Message.h"
+
+xPL_Message::xPL_Message()
+{
+    command = NULL;
+    command_count = 0;
+}
+
+xPL_Message::~xPL_Message()
+{
+    if(command != NULL)
+    {
+        free(command);
+    }
+}
+
+/**
+ * \brief       Set source of the message (optional)
+ * \param    _vendorId         vendor id.
+ * \param    _deviceId         device id.
+ * \param    _instanceId      instance id.
+ */
+void xPL_Message::SetSource(char * _vendorId, char * _deviceId, char * _instanceId)
+{
+    memcpy(source.vendor_id, _vendorId, XPL_VENDOR_ID_MAX + 1);
+    memcpy(source.device_id, _deviceId, XPL_DEVICE_ID_MAX + 1);
+    memcpy(source.instance_id, _instanceId, XPL_INSTANCE_ID_MAX + 1);
+}
+
+/**
+ * \brief       Set Target of the message
+ * \details   insert "*" into _vendorId to broadcast the message
+ * \param    _vendorId         vendor id.
+ * \param    _deviceId         device id.       (optional)
+ * \param    _instanceId      instance id.    (optional)
+ */
+void xPL_Message::SetTarget(const char * _vendorId, const char * _deviceId, const char * _instanceId)
+{
+    memcpy(target.vendor_id, _vendorId, XPL_VENDOR_ID_MAX + 1);
+    if(_deviceId != NULL) memcpy(target.device_id, _deviceId, XPL_DEVICE_ID_MAX + 1);
+    if(_instanceId != NULL) memcpy(target.instance_id, _instanceId, XPL_INSTANCE_ID_MAX + 1);
+}
+
+/**
+ * \brief       Set Schema of the message
+  * \param   _classId       Class
+ * \param    _typeId         Type
+  */
+void xPL_Message::SetSchema(const char * _classId, const char * _typeId)
+{
+    memcpy(schema.class_id, _classId, XPL_CLASS_ID_MAX + 1);
+    memcpy(schema.type_id, _typeId, XPL_TYPE_ID_MAX + 1);
+}
+
+/**
+ * \brief       Create a new command/value pair
+ * \details   Check if maximun command is reach and add memory to command array
+ */
+bool xPL_Message::CreateCommand()
+{
+    struct_command  *ncommand;
+
+    // Maximun command reach
+    // To avoid oom, we arbitrary accept only XPL_MESSAGE_COMMAND_MAX command
+    if(command_count > XPL_MESSAGE_COMMAND_MAX)
+        return false;
+        
+    ncommand = (struct_command*)realloc ( command, (command_count + 1) * sizeof(struct_command) );
+    
+    if (ncommand != NULL) {
+        command = ncommand;
+        command_count++;
+        return true;
+    }
+    else
+        return false;
+}
+
+/**
+ * \brief       Add a command to the message's body
+ * \details   PROGMEM Version
+ * \param    _name         name of the command
+ * \param    _value         value of the command
+ */
+bool xPL_Message::AddCommand(const char* _name, const char* _value)
+{
+    if(!CreateCommand()) return false;
+
+    struct_command newcmd;
+    memcpy(newcmd.name, _name, XPL_NAME_LENGTH_MAX + 1);
+    memcpy(newcmd.value, _value, XPL_VALUE_LENGTH_MAX + 1);
+    command[command_count-1] = newcmd;
+    return true;
+}
+
+/**
+ * \brief       Add a command to the message's body
+ * \details   char* Version
+ * \param    _name         name of the command
+ * \param    _value         value of the command
+ */
+bool xPL_Message::AddCommand(char* _name, char* _value)
+{
+    if(!CreateCommand()) return false;
+
+    struct_command newcmd;
+    memcpy(newcmd.name, _name, XPL_NAME_LENGTH_MAX + 1);
+    memcpy(newcmd.value, _value, XPL_VALUE_LENGTH_MAX + 1);
+    command[command_count-1] = newcmd;
+    return true;
+}
+
+/**
+ * \brief       Convert xPL_Message to char* buffer
+ */
+char message_buffer[XPL_MESSAGE_BUFFER_MAX];
+char* xPL_Message::toString()
+{
+  int pos;
+
+  clearStr(message_buffer);
+
+  switch(type)
+  {
+    case (XPL_CMND):
+      pos = sprintf(message_buffer, "xpl-cmnd");
+      break;
+    case (XPL_STAT):
+      pos = sprintf(message_buffer, "xpl-stat");
+      break;
+    case (XPL_TRIG):
+      pos = sprintf(message_buffer, "xpl-trig");
+      break;
+  }
+
+  pos += sprintf(message_buffer + pos, "\n{\nhop=1\nsource=%s-%s.%s\ntarget=", source.vendor_id, source.device_id, source.instance_id);
+
+  if(memcmp(target.vendor_id,"*", 1) == 0)  // check if broadcast message
+  {
+    pos += sprintf(message_buffer + pos, "*\n}\n");
+  }
+  else
+  {
+    pos += sprintf(message_buffer + pos, "%s-%s.%s\n}\n",target.vendor_id, target.device_id, target.instance_id);
+  }
+
+  pos += sprintf(message_buffer + pos, "%s.%s\n{\n",schema.class_id, schema.type_id);
+
+  for (short i=0; i<command_count; i++)
+  {
+    pos += sprintf(message_buffer + pos, "%s=%s\n", command[i].name, command[i].value);
+  }
+
+  sprintf(message_buffer + pos, "}\n");
+
+  return message_buffer;
+}
+
+bool xPL_Message::IsSchema(char* _classId, char* _typeId)
+{
+  if (strcmp(schema.class_id, _classId) == 0)
+  {
+    if (strcmp(schema.type_id, _typeId) == 0)
+    {
+      return true;
+    }   
+  }  
+
+  return false;
+}
+
+/**
+ * \brief       Check the message's schema
+  * \param   _classId        class
+ * \param    _typeId         type
+ */
+bool xPL_Message::IsSchema(const char* _classId, const char* _typeId)
+{
+    if (strncmp(schema.class_id, _classId, XPL_CLASS_ID_MAX) == 0)
+      {
+        if (strncmp(schema.type_id, _typeId, XPL_TYPE_ID_MAX) == 0)
+        {
+          return true;
+        }
+      }
+
+      return false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xPL_Message.h	Tue Oct 09 17:37:05 2018 +0000
@@ -0,0 +1,76 @@
+/*
+ * xPL.Arduino v0.1, xPL Implementation for Arduino
+ *
+ * This code is parsing a xPL message stored in 'received' buffer
+ * - isolate and store in 'line' buffer each part of the message -> detection of EOL character (DEC 10)
+ * - analyse 'line', function of its number and store information in xpl_header memory
+ * - check for each step if the message respect xPL protocol
+ * - parse each command line
+ *
+ * Copyright (C) 2012 johan@pirlouit.ch, olivier.lebrun@gmail.com
+ * Original version by Gromain59@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#ifndef xPLMessage_h
+#define xPLMessage_h
+ 
+//#include "Arduino.h"
+#include "mbed.h"
+#include "xPL_utils.h"
+
+#define XPL_CMND 1
+#define XPL_STAT 2
+#define XPL_TRIG 3
+
+#define XPL_MESSAGE_BUFFER_MAX           256  // going over 256 would mean changing index from byte to int
+#define XPL_MESSAGE_COMMAND_MAX          10
+
+class xPL_Message
+{
+    public:
+        short type;                 // 1=cmnd, 2=stat, 3=trig
+        short hop;              // Hop count
+        
+        struct_id source;           // source identification
+        struct_id target;           // target identification
+
+        struct_xpl_schema schema;
+        struct_command *command;
+        //byte command_count;
+        short command_count;
+
+        bool AddCommand(const char *,const char *);
+        bool AddCommand(char*, char*);
+        
+        xPL_Message();
+        ~xPL_Message();
+
+        char *toString();
+        
+        bool IsSchema(char*, char*);
+        bool IsSchema(const char*, const char*);
+    
+        void SetSource(char *, char *, char *);  // define my source
+        void SetTarget(const char *, const char * = NULL, const char * = NULL);
+        void SetSchema(const char *, const char *);
+        
+        
+    private:
+        bool CreateCommand();
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xPL_utils.cpp	Tue Oct 09 17:37:05 2018 +0000
@@ -0,0 +1,38 @@
+/*
+ * xPL.Arduino v0.1, xPL Implementation for Arduino
+ *
+ * This code is parsing a xPL message stored in 'received' buffer
+ * - isolate and store in 'line' buffer each part of the message -> detection of EOL character (DEC 10)
+ * - analyse 'line', function of its number and store information in xpl_header memory
+ * - check for each step if the message respect xPL protocol
+ * - parse each command line
+ *
+ * Copyright (C) 2012 johan@pirlouit.ch, olivier.lebrun@gmail.com
+ * Original version by Gromain59@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+ 
+#include "xPL_utils.h"
+
+// Function to clear a string
+void clearStr (char* str)
+{
+    int len = strlen(str);
+    for (short c = 0; c < len; c++)
+    {
+        str[c] = 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xPL_utils.h	Tue Oct 09 17:37:05 2018 +0000
@@ -0,0 +1,74 @@
+/*
+ * xPL.Arduino v0.1, xPL Implementation for Arduino
+ *
+ * This code is parsing a xPL message stored in 'received' buffer
+ * - isolate and store in 'line' buffer each part of the message -> detection of EOL character (DEC 10)
+ * - analyse 'line', function of its number and store information in xpl_header memory
+ * - check for each step if the message respect xPL protocol
+ * - parse each command line
+ *
+ * Copyright (C) 2012 johan@pirlouit.ch, olivier.lebrun@gmail.com
+ * Original version by Gromain59@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+ 
+#ifndef xPLutil_h
+#define xPLutil_h
+
+//#include "Arduino.h"
+#include "mbed.h"
+#include <string.h>
+
+#define XPL_VENDOR_ID_MAX       8
+#define XPL_DEVICE_ID_MAX       20
+#define XPL_INSTANCE_ID_MAX     16
+#define XPL_CLASS_ID_MAX        8
+#define XPL_TYPE_ID_MAX         8
+#define XPL_NAME_LENGTH_MAX     20
+#define XPL_VALUE_LENGTH_MAX    128  // should be 128 but need to spare RAM
+
+#define XPL_HOP_COUNT_PARSER    "hop=%d"
+#define XPL_SOURCE_PARSER       "source=%20[^-]-%20[^'.'].%16s"
+#define XPL_TARGET_PARSER       "target=%20[^-]-%20[^'.'].%16s"
+#define XPL_SCHEMA_PARSER       "%8[^'.'].%8s"
+// 32 shall match XPL_VALUE_LENGTH_MAX
+#define XPL_COMMAND_PARSER      "%20[^'=']=%32s"
+
+typedef struct struct_id struct_id;
+struct struct_id            // source or target
+{
+    char vendor_id[XPL_VENDOR_ID_MAX+1];        // vendor id
+    char device_id[XPL_DEVICE_ID_MAX+1];        // device id
+    char instance_id[XPL_INSTANCE_ID_MAX+1];        // instance id
+};
+
+typedef struct struct_xpl_schema struct_xpl_schema;
+struct struct_xpl_schema
+{
+    char class_id[XPL_CLASS_ID_MAX+1];  // class of schema (x10, alarm...)
+    char type_id[XPL_TYPE_ID_MAX+1];    // type of schema (basic...)
+};
+
+typedef struct struct_command struct_command;
+struct struct_command           // source or target
+{
+    char name[XPL_NAME_LENGTH_MAX+1];       // vendor id
+    char value[XPL_VALUE_LENGTH_MAX+1];     // device id
+};
+
+void clearStr (char* str);
+
+#endif