mbed xpl base example

Files at this revision

API Documentation at this revision

Comitter:
richnash
Date:
Mon Jul 30 14:10:27 2018 +0000
Commit message:
base xpl listener sender

Changed in this revision

Watchdog.cpp Show annotated file Show diff for this revision Revisions of this file
Watchdog.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
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
diff -r 000000000000 -r 0fc77069cf70 Watchdog.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Watchdog.cpp	Mon Jul 30 14:10:27 2018 +0000
@@ -0,0 +1,132 @@
+/// @file Watchdog.cpp provides the interface to the Watchdog module
+///
+/// This provides basic Watchdog service for the mbed. You can configure
+/// various timeout intervals that meet your system needs. Additionally,
+/// it is possible to identify if the Watchdog was the cause of any 
+/// system restart.
+/// 
+/// Adapted from Simon's Watchdog code from http://mbed.org/forum/mbed/topic/508/
+///
+/// @note Copyright © 2011 by Smartware Computing, all rights reserved.
+///     This software may be used to derive new software, as long as
+///     this copyright statement remains in the source file.
+/// @author David Smart
+///
+/// @note Copyright © 2015 by NBRemond, all rights reserved.
+///     This software may be used to derive new software, as long as
+///     this copyright statement remains in the source file.
+///
+///     Added support for STM32 Nucleo platforms
+///
+/// @author Bernaérd Remond
+///
+
+//#define LPC
+#define ST_NUCLEO
+
+
+#include "mbed.h"
+#include "Watchdog.h"
+
+
+/// Watchdog gets instantiated at the module level
+Watchdog::Watchdog() {
+#ifdef LPC    
+    wdreset = (LPC_WDT->WDMOD >> 2) & 1;    // capture the cause of the previous reset
+#endif
+#ifdef ST_NUCLEO
+    // capture the cause of the previous reset
+    /* Check if the system has resumed from IWDG reset */
+    if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST)) {
+    //if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST)) {
+        wdreset = true;
+    }
+    else {
+        wdreset = false; 
+    }
+        
+//        wdreset = false; 
+#endif
+
+}
+
+/// Load timeout value in watchdog timer and enable
+void Watchdog::Configure(float timeout) {
+#ifdef LPC    
+    LPC_WDT->WDCLKSEL = 0x1;                // Set CLK src to PCLK
+    uint32_t clk = SystemCoreClock / 16;    // WD has a fixed /4 prescaler, PCLK default is /4
+    LPC_WDT->WDTC = (uint32_t)(timeout * (float)clk);
+    LPC_WDT->WDMOD = 0x3;                   // Enabled and Reset
+#endif   
+#ifdef ST_NUCLEO
+    // see http://embedded-lab.com/blog/?p=9662
+    #define LsiFreq (45000)
+    
+    uint16_t PrescalerCode;
+    uint16_t Prescaler;
+    uint16_t ReloadValue;
+    float Calculated_timeout;
+    
+    if ((timeout * (LsiFreq/4)) < 0x7FF) {
+        PrescalerCode = IWDG_PRESCALER_4;
+        Prescaler = 4;
+    }
+    else if ((timeout * (LsiFreq/8)) < 0xFF0) {
+        PrescalerCode = IWDG_PRESCALER_8;
+        Prescaler = 8;
+    }
+    else if ((timeout * (LsiFreq/16)) < 0xFF0) {
+        PrescalerCode = IWDG_PRESCALER_16;
+        Prescaler = 16;
+    }
+    else if ((timeout * (LsiFreq/32)) < 0xFF0) {
+        PrescalerCode = IWDG_PRESCALER_32;
+        Prescaler = 32;
+    }
+    else if ((timeout * (LsiFreq/64)) < 0xFF0) {
+        PrescalerCode = IWDG_PRESCALER_64;
+        Prescaler = 64;
+    }
+    else if ((timeout * (LsiFreq/128)) < 0xFF0) {
+        PrescalerCode = IWDG_PRESCALER_128;
+        Prescaler = 128;
+    }
+    else {
+        PrescalerCode = IWDG_PRESCALER_256;
+        Prescaler = 256;
+    }
+    
+    // specifies the IWDG Reload value. This parameter must be a number between 0 and 0x0FFF.
+    ReloadValue = (uint32_t)(timeout * (LsiFreq/Prescaler));
+    
+    Calculated_timeout = ((float)(Prescaler * ReloadValue)) / LsiFreq;
+    //printf("WATCHDOG set with prescaler:%d reload value: 0x%X - timeout:%f\r\n",Prescaler, ReloadValue, Calculated_timeout);
+    
+    IWDG->KR = 0x5555; //Disable write protection of IWDG registers      
+    IWDG->PR = PrescalerCode;      //Set PR value      
+    IWDG->RLR = ReloadValue;      //Set RLR value      
+    IWDG->KR = 0xAAAA;    //Reload IWDG      
+    IWDG->KR = 0xCCCC;    //Start IWDG - See more at: http://embedded-lab.com/blog/?p=9662#sthash.6VNxVSn0.dpuf       
+#endif
+ 
+    Service();
+}
+
+/// "Service", "kick" or "feed" the dog - reset the watchdog timer
+/// by writing this required bit pattern
+void Watchdog::Service() {
+#ifdef LPC    
+    LPC_WDT->WDFEED = 0xAA;
+    LPC_WDT->WDFEED = 0x55;
+#endif    
+#ifdef ST_NUCLEO
+    IWDG->KR = 0xAAAA;         //Reload IWDG - See more at: http://embedded-lab.com/blog/?p=9662#sthash.6VNxVSn0.dpuf
+#endif
+}
+
+/// get the flag to indicate if the watchdog causes the reset
+bool Watchdog::WatchdogCausedReset() {
+    return wdreset;
+}
+
+
diff -r 000000000000 -r 0fc77069cf70 Watchdog.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Watchdog.h	Mon Jul 30 14:10:27 2018 +0000
@@ -0,0 +1,101 @@
+/// @file Watchdog.h provides the interface to the Watchdog module
+///
+/// This provides basic Watchdog service for the mbed. You can configure
+/// various timeout intervals that meet your system needs. Additionally,
+/// it is possible to identify if the Watchdog was the cause of any 
+/// system restart, permitting the application code to take appropriate
+/// behavior.
+/// 
+/// Adapted from Simon's Watchdog code from http://mbed.org/forum/mbed/topic/508/
+///
+/// @note Copyright &copy; 2011 by Smartware Computing, all rights reserved.
+///     This software may be used to derive new software, as long as
+///     this copyright statement remains in the source file.
+/// @author David Smart
+///
+/// History
+/// \li v1.00 - 20110616: initial release with some documentation improvements
+///
+#ifndef WATCHDOG_H
+#define WATCHDOG_H
+#include "mbed.h"
+
+/// The Watchdog class provides the interface to the Watchdog feature
+///
+/// Embedded programs, by their nature, are usually unattended. If things
+/// go wrong, it is usually important that the system attempts to recover.
+/// Aside from robust software, a hardware watchdog can monitor the
+/// system and initiate a system reset when appropriate.
+///
+/// This Watchdog is patterned after one found elsewhere on the mbed site,
+/// however this one also provides a method for the application software
+/// to determine the cause of the reset - watchdog or otherwise.
+///
+/// example:
+/// @code
+/// Watchdog wd;
+///
+/// ...
+/// main() {
+///    if (wd.WatchdogCausedReset())
+///        pc.printf("Watchdog caused reset.\r\n");
+///      
+///    wd.Configure(3.0);       // sets the timeout interval
+///    for (;;) {
+///         wd.Service();       // kick the dog before the timeout
+///         // do other work
+///    }
+/// }
+/// @endcode
+///
+class Watchdog {
+public:
+    /// Create a Watchdog object
+    ///
+    /// example:
+    /// @code
+    /// Watchdog wd;    // placed before main
+    /// @endcode
+    Watchdog();
+    
+    /// Configure the timeout for the Watchdog
+    ///
+    /// This configures the Watchdog service and starts it. It must
+    /// be serviced before the timeout, or the system will be restarted.
+    ///
+    /// example:
+    /// @code
+    ///     ...
+    ///     wd.Configure(1.4);  // configure for a 1.4 second timeout
+    ///     ...
+    /// @endcode
+    ///
+    /// @param[in] timeout in seconds, as a floating point number
+    /// @returns none
+    ///
+    void Configure(float timeout);
+    
+    /// Service the Watchdog so it does not cause a system reset
+    ///
+    /// example:
+    /// @code
+    ///    wd.Service();
+    /// @endcode
+    /// @returns none
+    void Service();
+    
+    /// WatchdogCausedReset identifies if the cause of the system
+    /// reset was the Watchdog
+    ///
+    /// example:
+    /// @code
+    ///    if (wd.WatchdogCausedReset())) {
+    /// @endcode
+    ///
+    /// @returns true if the Watchdog was the cause of the reset
+    bool WatchdogCausedReset();
+private:
+    bool wdreset;
+};
+
+#endif // WATCHDOG_H
\ No newline at end of file
diff -r 000000000000 -r 0fc77069cf70 main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Mon Jul 30 14:10:27 2018 +0000
@@ -0,0 +1,435 @@
+
+// ##################################################################################
+//      XPL Test Program
+// ##################################################################################
+
+#if !FEATURE_LWIP
+    #error [NOT_SUPPORTED] LWIP not supported for this target
+#endif
+
+// -----------------------------------------------------------
+// MBED Headers
+#include "mbed.h"
+#include "EthernetInterface.h"
+#include "rtos.h"
+
+// -----------------------------------------------------------
+// My Headers
+#include "Watchdog.h"
+#include "xPL.h"
+
+// -----------------------------------------------------------
+// defines
+#define UDP_PORT            3865
+#define UDP_BUFFER_SIZE     500
+
+// -----------------------------------------------------------
+// global variables
+Watchdog wd;
+xPL xpl;
+
+DigitalOut led1(LED1);
+DigitalOut led2(LED2);
+DigitalOut led3(LED3);
+DigitalOut led4(LED4);
+
+Serial logger(SERIAL_TX, SERIAL_RX);
+
+EthernetInterface   gEth;
+UDPSocket           _socket;
+UDPSocket           _socketSender;
+char                in_buffer[UDP_BUFFER_SIZE];
+
+// -----------------------------------------------------------
+// 
+// -----------------------------------------------------------
+void terminal_error_state( void )
+{
+    led1=0;
+    led2=0;
+    led3=0;
+    logger.printf("\r\n\r\nJUST TO BE CLEAR - THIS IS BAD BAD NEWS !!! \r\n");
+    logger.printf("\r\n\r\n---- WAITING FOR THE WATCHDOG TO RESET US ---- \r\n");
+    while( 1 )
+        {
+        led1 = !led1;
+        led2 = !led2;
+        led3 = !led3;
+        wait(0.3);
+        }
+}
+
+// -----------------------------------------------------------
+// 
+// -----------------------------------------------------------
+static bool DeviceConnect()
+{
+    int retries = 10;
+    int rc = 0;
+    
+    while (retries--) 
+        {
+        rc = gEth.connect();
+        if (rc == 0) {
+            // done
+            return true;
+            }
+        else
+            {
+            logger.printf("Connecting... ERR %d\r\n", rc);
+            }
+        }
+    
+    logger.printf("Connecting... FAILED\r\n");
+    return false;
+}
+
+// -----------------------------------------------------------
+// 
+// -----------------------------------------------------------
+static bool DeviceEthStartup()
+{
+   int rc = 0;
+   
+   // get ethernet up !
+    if( !DeviceConnect() ) terminal_error_state();
+    
+    // Output the network address
+    const char *ip = gEth.get_ip_address();
+    const char *netmask = gEth.get_netmask();
+    const char *gateway = gEth.get_gateway();
+    printf("IP address: %s\r\n", ip ? ip : "None");
+    printf("Netmask: %s\r\n", netmask ? netmask : "None");
+    printf("Gateway: %s\r\n", gateway ? gateway : "None");
+    
+    // setup a UDP listener
+    _socket.set_blocking(true);
+    _socket.set_timeout(3000);
+    //_socket.set_broadcasting(true);
+    
+    // open the socket
+    rc = _socket.open((NetworkInterface*)&gEth);
+    if( rc != 0)
+        {
+        logger.printf("UDPSocket RECEIVER OPEN ERROR   (rc=%d)\r\n", rc);
+        terminal_error_state();
+        }
+    
+    // bind the XPL port
+    rc = _socket.bind(UDP_PORT);
+    logger.printf("Binding port %d\r\n", UDP_PORT);
+    if( rc != 0)
+        {
+        logger.printf("UDPSocket BIND ERROR   (rc=%d)\r\n", rc);
+        terminal_error_state();
+        }
+    
+    // open the sender socket
+    rc = _socketSender.open((NetworkInterface*)&gEth);
+    if( rc != 0)
+        {
+        logger.printf("UDPSocket SENDER OPEN ERROR   (rc=%d)\r\n", rc);
+        terminal_error_state();
+        }
+    //_socketSender.set_broadcasting(true);
+    
+    return true;
+}
+
+
+// -----------------------------------------------------------
+// 
+// -----------------------------------------------------------
+void SendUdPMessage(char *buffer)
+{
+    //const char * msg = "xpl-stat{\nhop=1\nsource=NIHH-NUCLEO1.100040\ntarget=*\n}\nsensor.basic\n{\ncurrent=133\ndevice=NUCLEO1\n}";
+    
+    //SocketAddress *_sAddr = new SocketAddress("255.255.255.255",UDP_PORT);
+    //_socketSender.sendto( _sAddr, (const void *)buffer, strlen( buffer ) );
+    
+    //int rc = _socketSender.sendto( "10.0.0.51", UDP_PORT, (const void *)buffer, strlen( buffer ) );
+    //logger.printf("[SEND rc=%d] %s\r\n", rc, buffer);
+    
+    //int rc = _socketSender.sendto( "255.255.255.255", UDP_PORT, (const void *)msg, strlen( msg ) );
+    //logger.printf("[SEND rc=%d] %s\r\n", rc, buffer);
+    
+    //int rc = _socketSender.sendto( "10.0.0.53", 3800, (const void *)buffer, strlen( buffer ) );
+    //int rc = _socketSender.sendto( "10.0.0.53", UDP_PORT, (const void *)buffer, strlen( buffer ) );
+    int rc = _socketSender.sendto( "255.255.255.255", UDP_PORT, (const void *)buffer, strlen( buffer ) );
+    
+    //logger.printf("[SEND rc=%d] buffer[%d]\r\n", rc, strlen(buffer) );
+    logger.printf("[SEND rc=%d] buffer[%d]\r\n%s\r\n", rc, strlen(buffer), buffer);
+    
+/*
+    SocketAddress *_socketAddress;
+    _socketAddress = new SocketAddress((NetworkInterface*)&gEth, "10.0.0.255", UDP_PORT);
+    int rc = _socketSender.sendto( _socketAddress, (const void *)msg, strlen( msg ) );
+    logger.printf("[SEND rc=%d] %s\r\n", rc, msg);
+*/
+}
+
+/* *******************************************************
+
+xpl-stat
+{
+hop=1
+source=NIHH-NUCLEO1.100043
+target=*
+}
+clock.update
+{
+time=20180730113315
+}
+
+xpl-stat
+{
+hop=1
+source=NIHH-NUCLEO1.100043
+target=*
+}
+hbeat.app
+{
+interval=300
+port=3865
+remote-ip=8.8.8.8
+version=1.0
+}
+
+******************************************************* */
+
+
+
+
+// -----------------------------------------------------------
+// 
+// -----------------------------------------------------------
+void SendXPLint(char *Device, int iCurrent )
+{
+//    xPL_Message _message;
+//    _message.SetTarget( "*" );
+//    _message.AddCommand("device", "xxxxx");
+//    _message.AddCommand("current", "0");
+//    xpl.SendMessage( &_message, true );
+}
+
+
+// -----------------------------------------------------------
+// 
+// -----------------------------------------------------------
+void SendXPLString(char *Device, char *szCurrent )
+{
+//    xPL_Message _message;
+//    _message.SetTarget( "*" );
+//    _message.AddCommand("device", "xxxxx");
+//    _message.AddCommand("current", "0");
+//    xpl.SendMessage( &_message, true );
+}
+
+
+// -----------------------------------------------------------
+// 
+// -----------------------------------------------------------
+void AfterParseAction(xPL_Message * message)
+{
+    /*    
+    if (xpl.TargetIsMe(message))
+    {
+      if (message->IsSchema("lighting", "basic"))
+      {
+        Serial.println("is lighting.basic");
+      }
+    }
+    */
+    
+    logger.printf("[%s]\r\n", message->source.device_id );
+    
+    // do we have any data ???  maybe the parse failed....
+    if( message->command_count == 0 )
+        {
+        // 
+        logger.printf("+++++++++++++\r\n%s\r\n", in_buffer );
+        }
+    else
+        {
+        // output the message data  
+        for (short i=0; i<message->command_count; i++)
+            {
+            //logger.printf("%s=%s\r\n", message->command[i].name, message->command[i].value);
+            }
+        }
+    
+    // output the full msg!
+    //logger.printf("%s\r\n", message->toString());
+}
+
+
+
+// -----------------------------------------------------------
+// 
+// -----------------------------------------------------------
+void Receiver_Thread(void const *argument)
+{
+    SocketAddress   _sAddr;
+    int             iNoDataCount = 0;
+    
+    // loop forever
+    while(1)
+        {
+        // receive from the udp socket
+        led2 = 0;
+        int n = _socket.recvfrom(&_sAddr, in_buffer, UDP_BUFFER_SIZE);
+        //led2 = 1;
+        
+        // see what weve got !
+        if (n == NSAPI_ERROR_WOULD_BLOCK)
+            {
+            // timeout with no data...
+            ++iNoDataCount;
+            logger.printf("-- NoData (TIMEOUT)\r\n");
+            led2 = 0;
+            }
+        else if (n < 0)
+            {
+            // some other error
+            ++iNoDataCount;
+            logger.printf("-- NoData (ERR) %i\r\n", n);
+            led2 = 0;
+            }
+        else if( n > 0)
+            {
+            // good data :)
+            iNoDataCount = 0;
+            in_buffer[n] = '\0';
+            // logger.printf("[%d bytes] %s:%d\r\n", n, _sAddr.get_ip_address(), _sAddr.get_port());
+            // logger.printf("%s\r\n", in_buffer);
+            led2 = 1;
+            
+            // try and parse the msg
+            xpl.ParseInputMessage(in_buffer);
+            }
+        
+        // have we stopped receiving data ?!?!?
+        if( iNoDataCount > 20 )
+            {
+            logger.printf("-------- Arghhhh NO DATA !!! ---------\r\n");
+            
+            // maybe - looks dodgy at least...  try the reset
+            //gEth.disconnect();
+            //wait(0.5);
+            //DeviceEthStartup();
+            //Thread::wait(1000);
+            terminal_error_state();
+            }
+        
+        // stroke the WD
+        wd.Service();
+        }
+}
+
+
+// -----------------------------------------------------------
+// 
+// -----------------------------------------------------------
+int main()
+{
+    int iAppLoop=0;
+    int iXPLdata=0;
+    
+    // FIRST THINGS FIRST!!!  set the WD timeout interval...
+    // enough to get up and running!
+    wd.Configure(1000.0);
+    
+    // setup the pc serial logger
+    logger.baud(115200);
+    logger.printf("\r\n\r\n<<<<<<<<< Basic UDP XPL Listener >>>>>>>>>>\r\n");
+    
+    led1 = 0;
+    led2 = 0;
+    led3 = 0;
+    led4 = 0;
+    
+    // WatchDog Startup Information
+    logger.printf("\r\n------------------------------------------------------\r\n" );
+    logger.printf("[SystemCoreClock] %d Hz\r\n", SystemCoreClock);
+    logger.printf("[Reset reason] ");
+    if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST)) logger.printf("Independant-Watchdog  ");
+    if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST)) logger.printf("Window-Watchdog  ");
+    if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST)) logger.printf("Low-Power-Reset  ");
+    if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST)) logger.printf("Software-Reset  ");
+    if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST)) logger.printf("Power-On/Down-Reset  ");
+    if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST)) logger.printf("Reset-pin  ");
+    if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) logger.printf("Brownout  ");
+    if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY)) logger.printf("Low-speed-internal-clock-ready  ");
+    __HAL_RCC_CLEAR_RESET_FLAGS();
+    logger.printf("\r\n------------------------------------------------------\r\n");
+    
+    if (wd.WatchdogCausedReset())
+        {
+        logger.printf("<<Watchdog caused reset>>\r\n");
+        led3 = 1;
+        }
+    else
+        {
+        logger.printf("<<Normal Startup>>\r\n");
+        led1 = 1;
+        led2 = 1;
+        led3 = 0;
+        }
+    logger.printf("------------------------------------------------------\r\n\r\n");
+    
+    // Fireup the Ethernet
+    DeviceEthStartup();
+    
+    // now set the WD to something to run with - between receving data
+    wd.Service();
+    wd.Configure(300.0);
+    
+    // setup xpl
+    xpl.SendExternal = &SendUdPMessage;  // pointer to the send callback
+    xpl.AfterParseAction = &AfterParseAction;  // pointer to a post parsing action callback 
+    xpl.SetSource( "NIHH", "NUCLEO1", "100043"); // parameters for hearbeat message
+    
+    // kick off the XPL receiver thread to run in the background
+    Thread thread1(Receiver_Thread, NULL, osPriorityHigh); //, DEFAULT_STACK_SIZE);
+    
+    // start running the main processing stuff here
+    while (true) 
+        {
+        led1 = 0;
+        Thread::wait(2000);
+        led1 = 1;
+        logger.printf("*\r\n");
+        Thread::wait(100);
+        
+        // allow xpl to do its thing...
+        xpl.Process();
+        
+        // every 10 loops send an XPL msg
+        ++iAppLoop;
+        if( iAppLoop >= 5 )
+            {
+            xPL_Message _message;
+            
+            /*
+            _message.SetSchema( "clock", "update" );
+            _message.SetTarget( "*" );
+            _message.type = XPL_STAT;
+            _message.hop = 1;
+            _message.AddCommand("time", "20180730113315");
+            xpl.SendMessage( (xPL_Message *)&_message, true );
+            */
+            
+            _message.SetSchema( "sensor", "basic" );
+            _message.SetTarget( "*" );
+            _message.type = XPL_STAT;
+            _message.hop = 1;
+            _message.AddCommand("device", "test1");
+            _message.AddCommand("type", "test");
+            _message.AddCommand("current", "1");
+            xpl.SendMessage( (xPL_Message *)&_message, true );
+            
+            // reset
+            iAppLoop = 0;
+            }
+        }
+}
diff -r 000000000000 -r 0fc77069cf70 mbed-os.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os.lib	Mon Jul 30 14:10:27 2018 +0000
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/mbed-os/#50bd61a4a72332baa6b1bac6caccb44dc5423309
diff -r 000000000000 -r 0fc77069cf70 xPL.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xPL.cpp	Mon Jul 30 14:10:27 2018 +0000
@@ -0,0 +1,399 @@
+/*
+ * 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);
+    
+    //(*SendExternal)(buffer);
+    SendMessage(buffer);
+}
+
+/**
+ * \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
diff -r 000000000000 -r 0fc77069cf70 xPL.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xPL.h	Mon Jul 30 14:10:27 2018 +0000
@@ -0,0 +1,102 @@
+/*
+ * 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);
+
+  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
diff -r 000000000000 -r 0fc77069cf70 xPL_Message.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xPL_Message.cpp	Mon Jul 30 14:10:27 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;
+}
diff -r 000000000000 -r 0fc77069cf70 xPL_Message.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xPL_Message.h	Mon Jul 30 14:10:27 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
diff -r 000000000000 -r 0fc77069cf70 xPL_utils.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xPL_utils.cpp	Mon Jul 30 14:10:27 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;
+    }
+}
diff -r 000000000000 -r 0fc77069cf70 xPL_utils.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xPL_utils.h	Mon Jul 30 14:10:27 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