Navigation

    • Register
    • Login
    • OpenHardware.io
    • Categories
    • Recent
    • Tags
    • Popular
    1. Home
    2. soif
    3. Best
    • Profile
    • Following
    • Followers
    • Topics
    • Posts
    • Best
    • Groups

    Best posts made by soif

    • MySensors Php API class + command line script + Form

      As I needed a simple tool to test MySensors send/receive messages, I've created a basic php class, including a command line script , that can be used to send/receive mysensors messages directly to the Ethernet Gateway.

      You can use it in your php controllers, or directly on the command line by tapping commands like:

      ./mysensors_bin.php 192.168.0.240 set 12 0 V_STATUS 1
      

      This example send a command to the gateway at IP 192.168.0.240, to set the V_STATUS to 1, for Node 12, child 0...

      The project is at GitHub, and your contributions are most welcome !
      https://github.com/soif/MySensors_Php_API

      BTW I did my best to understand how MySensors messages worked, but without more documentation, I may have missed some things. So test it, and feel free to commit PR to the develop branch !

      HTH 😛

      posted in My Project
      soif
      soif
    • RFC : Lazy Submission to OpenHardware.io using GitHub

      Here is a proposal (RFC) to encourage developers to publish MySensors projects on Github, and get their projects published/updated on OpenHardware.io.

      IMHO, developers are lazy, and tracking / gathering project information from the forums, or asking developpers to submit on OpenHardware.io, is also a time consuming process for OH.io maintainers.

      What if developpers would just have to follow a very basic file structure, when publishing their projects on GitHub and then to just have to submit their repo URL to MySensors, to get their project AUTOMAGICALLY recorded/updated at OpenHardware.io.

      From a developper point of view :

      • developers love GitHub but hate filling forms or making documentations
      • most developers are LAZY (right?) and just asking them to follow easy conventions when publishing on GH is certainly not that hard, if clearly advertised on mysensors.org or OH.io.
      • just pushing new commits to GH, is really more funny/natural , than taking the time to go to OH.io, and submit modifications, new images, new code, etc...
      • others developpers could contribute to each others project using GH pull-requests, submitting issues, adding pictures, etc...

      From OpenHardware.io mainteners point of view :

      • the more way to submit projects, the more projects to get published
      • updates could be fully automated (developer push to GH, then OH.io can just automatically fetch new infos)
      • It don't prevent non-GH users to use the regular OH.io submission form if they wish
      • GH users would be more able/naturally prompted to contribute to each other projects
      • Don't re-invent the wheel for developpers interaction/collaboration, by just interfacing with GH
      • easy to implement

      So to make this more comprehensible, I've pushed a proposal of a Mysensors Template Project at:
      https://github.com/soif/mysensors_template

      and a demo showing how this could be easily parsed at :
      https://jsfiddle.net/soif/ek5j7pao/

      Obviously this is just a draft, and standards should be defined/fixed by OH.io maintainers...

      Sounds interesting for you developpers?
      And for you OH.io maintainers?

      HTH

      posted in Feature Requests
      soif
      soif
    • RE: MySensors Php API class + command line script + Form

      Currently Working (develop branch) on a convenient form to convert/send messages .

      Form

      It should certainly help developers to test their sensors/gateway 😉

      posted in My Project
      soif
      soif
    • RE: RFC : Lazy Submission to OpenHardware.io using GitHub

      Except adding a little bit more work to maintain changes in version, date, description, adding photos, etc... 😉 So as it is not used I this time , I will remove it.

      Anyway, It would be really easy to revert the (clean-up) commit, whenever you'd decide to use this suggested (or another similar) standard.

      posted in Feature Requests
      soif
      soif
    • Other Controller get disconnected when sending any command to the Eth. Gateway (v2.0)

      Short :

      I'm trying to send mysensors message to the ethernet gateway. I've also another Controller (domoticz) connected at the same time. Whatever message I type (even a simple CR) cause Domoticz to be immediatly disconnected.

      Long :

      As I knew that my 1.51 version is not supposed to support mutiple connection to the Eth gateway, I've downloaded the latest dev version from github (# 1f27c3d ) and compiled the GatewayW5100.ino example and just added at top of the file (around line 108, just before the first include):

      #define MY_GATEWAY_MAX_CLIENTS 3
      

      I suppose this makes the gateway able to handle multiple connections at the same time.

      It seems to work, as I can see messages printed on the telnet, while Domoticz is also connected, sending hearbeat messages.

      But sending any message, or just sending a CarriageReturn) from telnet, just make Domoticz to be immediately disconnected, with a message in its log file :

      MySensors: Connection reset!
      TCP: Reconnecting in 30 seconds...
      

      obviously letting him away for 30 seconds is not acceptable in production.

      According to Domoticz MySensors source code, the error may be caused by :

      (error == boost::asio::error::eof) ||
      (error == boost::asio::error::connection_reset)
      

      Questions:

      • Am I doing something wrong (my C++ understanding being very limited?
      • Is #define MY_GATEWAY_MAX_CLIENTS 3 supposed to work at this time?
      • Is this a bug from MySensors? From Domoticz?
      • Is it definitively not possible to send messages when another controller is also connected?

      Any help would be greatly appreciated ! 🙂

      posted in Troubleshooting
      soif
      soif
    • RE: Other Controller get disconnected when sending any command to the Eth. Gateway (v2.0)

      Yep, comments are often useful when we are not that lazy to include them 😄

      BTW I've found a topic telling about modifying the Eth library to get the Source IP, and being then able to support multiple clients. I don't know if it really helps.

      Also found this where the guy says that W5100 supports 4 sockets (so max 4 clients) but the Eth lib can NOT distinguish them if they are on the same port (might be a a workaround, to open 4 port instead of one, ie 5003,5004,5005,5006). He's finally submitted a patch against the Eth lib to allow 4 different sockets on the same port.... But the more interesting seems to be a comment by "Gene" who has posted complete sketch showing how to connect multiple telnet clients . Here is his code:

      /* exampleMultiTelnetServer.ino */
      /* 
       *  Created: May 21, 2015
       *
       *  Created by Gene Reeves to demonstrate how the W5100 eithenet handles multiple connections
       *  using a single server object instance.
       * 
       *  once the sketch is loaded on your arduino and you see the start up banner display in the
       *  Serial Monitor, fire up your favorite telnet client and connect a few instances.
       *  
       *  from the serial monitor you can send text to one or all clients. Use '*" for the ID to send
       *  to all, otherwise use the ID you wish to send to, using this format to send,
       *   "ID:TEDXT_TO_SEND".  That should make everything as clear as mud  :-)
       *
       *  Dependencies:
       *
       *  uses LinkedList class from here https://github.com/ivanseidel/LinkedList
       *  to store list of connected clients.
       *
       *  uses StringStream class from here https://gist.github.com/cmaglie/5883185
       *  Not sure why this was not a part of the StringObject to start with..
       *
       *  uses elapsedMillis class from here http://playground.arduino.cc/Code/ElapsedMillis
       *  much improved version (for Teensy brand mcu's) here https://www.pjrc.com/teensy/td_timing_elaspedMillis.html
       *  Unless I am mistaken, I beleive this code was originated by Paul Stoffregen of www.pjrc.com
       *  If you are not famialar with Paul or his Teensy microcontroller, you would do yourself a 
       *  solid to spend some time reviewing his code and you can't beat the Teensy 3.1 for 
       *  price/performance/support!!!   
       *  Teensy 3.1 - 32bit-ARM  mcu @ 72MHz (overclockable to 168MHz) w/ 256K flash / 64K ram / USB
       *  for under $20.  https://www.pjrc.com/teensy/index.html
       *
       *  BTW, I am in no way affilaited with Paul or PJRC, I just think he built an awesome mcu.
       *
       */
       
       
      #include <SPI.h>
      #include <LinkedList.h>
      #include <elapsedMillis.h>
      #include <StringStream.h>
      #include <Arduino.h>
      #include <EthernetUdp.h>
      #include <EthernetServer.h>
      #include <EthernetClient.h>
      #include <Ethernet.h>
      #include <Dns.h>
      #include <Dhcp.h>
      #include <IPAddress.h>
      #include <Server.h>
      #include <Client.h>
       
       
      /*  DEFINES  */
                            
      /*****************************************************/
      /*  Change the following to suite your environment.  */
      #define TELNET_SERVER_PORT 23
      #define TELNET_SERVER_MAC 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
      #define TELNET_SERVER_IP 192, 168, 1, 177
      #define TELNET_SERVER_GATEWAY 192, 168, 1, 1
      #define TELNET_SERVER_NETMASK 255, 255, 255, 0
      #define SERIAL_BAUD 115200
      #define WELCOME_BANNER_STRING "Welcome to MultiTelnetServer.\r\nType 'exit' to disconnect\r\n"
      #define GOODBYE_BANNER_STRING "Come Back Again,   Goodbye.."
       
      #define APP_BANNER_STRING "\r\n\r\n\tMultiTelnetServer Example\r\n  This example application strives to\r\ndemonstrate using the Wiznet W5x00\r\nEthernet Shield's Arduino library usage\r\nwhen serving mulitple concurrent\r\nconnects using a single 'server object'\r\ninstance."
      #define SERVER_LISTENING_STRING "\tServer Listening on"
       
      /******************************************************/
       
       
      /*
       *  This is required to add RemoteIP, RemotePort support to the EthernetClient class
       *  I stole it from here http://forum.arduino.cc/index.php?topic=210857.0
       */
      //namespace ethernet_fix
      //{
        #include <utility/w5100.h>
       
        template<typename Tag, typename Tag::type M>
        struct AccessMember{
          friend typename Tag::type get(Tag){ return M; }
        };
       
        struct EthernetClient_Socket{
          typedef uint8_t EthernetClient::*type;
          friend type get(EthernetClient_Socket);
        };
       
        template struct AccessMember < EthernetClient_Socket, &EthernetClient::_sock > ;
       
        IPAddress RemoteIP(EthernetClient &c){
          byte remoteIP[4];
          W5100.readSnDIPR(c.*get(EthernetClient_Socket()), remoteIP);
          return (remoteIP);
        }
       
        uint16_t RemotePort(EthernetClient &c){
          return W5100.readSnDPORT(c.*get(EthernetClient_Socket()));
        }
      //} // namespace "ethernet_fix"
      /*
       *  EthernetClientEx - Because there is still no RemoteIP or RemotePort
       *  properties built in to th current EthernetClient!!
       *
       */
       
      class EthernetClientEx :
        public EthernetClient
      {
      protected:
        uint8_t _sock; // hack to get access to socket #
      public:
        EthernetClientEx(const EthernetClient &orig) : EthernetClient(orig) {}
        IPAddress remoteIP() { return RemoteIP((EthernetClient &)(*this)); }
        int16_t remotePort() { return RemotePort((EthernetClient &)(*this)); }
       
        bool isSameSock(const EthernetClientEx &c) { return (_sock == c._sock); }
      };
       
       
      /*
       *  ClientItem is class to wrap EthernetClient for storage on linked list
       *  we will use it to add a ts (millis at connection time) and an index.
       *
       */
      class ClientItem
      {
      public:
        unsigned long timestamp;
        int index;
        size_t recv_cnt;
        String recv_buffer;
        EthernetClientEx *client;
       
        ClientItem() { timestamp = millis(); index = -1; client = (EthernetClientEx *)false; }
        virtual ~ClientItem() { delete client; /* release memory */}
         
        unsigned long elapsed(void) { unsigned long ts = millis(); return (ts - timestamp); }
      };
       
      /*
       *  ClientList is linkedlist for storing connected clients. 
       *  For now, we'll just use it to store our clients, but should
       *  expand this close to include testing all clients for disconnects,
       *  testing all clients for pending recv data and sending data
       *  to all connected clients.
       */
       
      class LinkedClientList :
        public LinkedList<ClientItem*> //ClientList;
      {
      public:
        //EthernetServer *server;
       
        ClientItem *getClientItem(int idx)
        {
          for (ListNode<ClientItem *> *n = root;n;n=n->next)
          { 
            if (n->data->index == idx)
              return n->data;
          }
          return (ClientItem *)0;
        }
         
        bool exists(const EthernetClientEx &ece)
        {
          for (ListNode<ClientItem *> *n = root; n; n = n->next)
            if (n->data->client->isSameSock(ece))
              return true;
          return false;
        }
       
        void drop(int idx)
        {
          ClientItem *cli;
          int real_idx = -1;
          int ci = 0;
          for (ListNode<ClientItem *> *n = root; n; n = n->next)
            if (n->data->index != idx)
              ci++;
            else
            {
              cli = n->data;
              real_idx = ci;
              break;
            }
       
          if (real_idx != -1)
          {
            if (cli->client->connected())
              cli->client->stop();
            cli->index = -1;
            remove(real_idx);
          }
        }
       
        size_t send(const String &s, int idx)
        {
          size_t ret = 0;
          ClientItem * cli = getClientItem(idx);
          if (cli)
          {
            ret = cli->client->print(s);
            cli->client->flush();
          }
          return ret;
        }
       
        size_t send2All(const String &s)
        {
          size_t ret = 0;
          for (ListNode<ClientItem *> *n = root; n; n = n->next)
          {
            ret += n->data->client->print(s);
            n->data->client->flush();
          }
          return ret;
        }
      };
       
      LinkedClientList ClientList;
       
      // Enter a MAC address and IP address for your controller below.
      // The IP address will be dependent on your local network.
      // gateway and subnet are optional:
      byte mac[] = { TELNET_SERVER_MAC };
      IPAddress ip(TELNET_SERVER_IP);
      IPAddress gateway(TELNET_SERVER_GATEWAY);
      IPAddress subnet(TELNET_SERVER_NETMASK);
       
      // buffer to hold input from console
      StringStream con_buffer("");
       
      // telnet defaults to port 23
      EthernetServer server(TELNET_SERVER_PORT);
       
      /* forward's */
      void server_check_new_connections(void);
      void server_check_client_recv(void);
      int server_accept(EthernetClientEx *enetcli);
      bool clientExists(const EthernetClientEx &c);
      bool test_for_cmd(const String &itm, const String &src);
      size_t console_recv(void);
      void proc_line(StringStream &ss);
       
      size_t client_recv(ClientItem *cli);
      void client_disconnecting(ClientItem *cli);
      void client_disconnected(ClientItem *cli);
       
      size_t send_to(int idx, const String &str);
      size_t send_to_all(const String &str);
       
      /*********************************************************/
      void setup()
      {
       
        /* add setup code here */
        // reserve some space for input buffer
        con_buffer.reserve(256);
       
        // Open serial communications and wait for port to open:
        Serial.begin(SERIAL_BAUD);
        delay(3000);  // 3 second delay for Serial to connect.
       
        pinMode(10, OUTPUT);
        digitalWrite(10, HIGH);
        pinMode(4, OUTPUT);
        digitalWrite(4, HIGH);
       
        // initialize the ethernet device
        Ethernet.begin(mac, ip, gateway, subnet);
        // start listening for clients
        server.begin();
       
        // Print "App Banner" and "Server Listening" notice.
        Serial.println(F(APP_BANNER_STRING));
        Serial.println(F(SERVER_LISTENING_STRING));
        Serial.print(F("\t\t"));
        Serial.print(ip);
        Serial.print(':');
        Serial.println(TELNET_SERVER_PORT);
        Serial.println(F("\r\n\r\n"));
       
      }
       
      void loop()
      {
       
        /* add main program code here */
       
        // check for any new clients
        server_check_new_connections();
       
        // give up a timeslice
        yield();
       
        // check to see if any of the clients 
        // have recv data pending
        if (ClientList.size() > 0)
          server_check_client_recv();
       
        // give up a timeslice
        yield();
       
        // check to see if console has any 
        // pending recv data.
        if (Serial.available())
          console_recv();
       
        // give up a timeslice
        yield();
      }
       
      /*********************************************************/
       
      size_t console_recv(void)
      {
        size_t ret = 0;
        int c = 0;
        while ((c = Serial.read()) >= 0)
        {
          if (c == 10)
          {
            proc_line(con_buffer);
            con_buffer = String("");
            con_buffer.begin();
            continue;
          }
          if (c != 13)
          {
            con_buffer += (char)c;
            ret++;
          }
        }
        return ret;
      }
       
      void proc_line(StringStream &ss)
      {
       
        //  Console input should be in the for of
        //  "ID:string_literal"
        //  where: 
        //    ID is client index to send to or "*" (all)
        //    string_literal is string to send
        //
        // check to ensure 2nd char is a ":"
        if ((ss.length() < 3) || (ss.charAt(1) != ':'))
        {
          Serial.print(F("Invalid Console Input \""));
          Serial.print(ss);
          Serial.print(F("\"\r\nPlease use the following format:\r\n\t"));
          Serial.println(F("\"ID:string_literal\"\r\n"));
          Serial.flush();
          return;
        }
        int idx = -1;
        Serial.print(F(" first char is '"));
        Serial.print(ss.charAt(0));
        Serial.print(F("',  "));
        if (ss.charAt(0) != '*')
        {
          idx = ss.parseInt();
        }
        Serial.print(F("idx="));
        Serial.print(idx);
        Serial.print(F(", indexOf(':')="));
        Serial.println(ss.indexOf(':'));
        ss = ss.substring(1 + ss.indexOf(':'));
        ss += F("\r\n");
        if (idx < 0)
          ClientList.send2All(ss);
          //send_to_all(ss);
        else
          ClientList.send(ss, idx);
      }
       
      size_t console_print_connected_count(void)
      {
        size_t ret =
          Serial.print(F("\t\t")) +
          Serial.print(ClientList.size()) +
          Serial.println(F(" connected client(s).\r\n"));
        Serial.flush();
        return ret;
      }
       
      bool test_for_cmd(const String &itm, const String &src)
      {
        String tst(src);
        tst.toLowerCase();
        return tst.startsWith(itm);
      }
       
      void server_check_new_connections(void)
      {
        EthernetClient obj = server.available();  
        if (obj)
        {
          // convert to EthernetClientEx
          EthernetClientEx new_client(obj);
          // is it a new connection?
          if (!ClientList.exists(new_client))
          {
            // accept new connection
            server_accept((EthernetClientEx *)&new_client);
       
            // send welcome banner
            new_client.println(F(WELCOME_BANNER_STRING));
            new_client.flush();
          }
        }
      }
       
      int server_accept(EthernetClientEx *enetcli)
      {
        // add itm to list
        ClientItem *itm = new ClientItem();
        itm->timestamp = millis();
        itm->index = ClientList.size();
        itm->client = new EthernetClientEx((*enetcli));
        ClientList.add(itm);
       
        // print notice on console
        Serial.print(F("Accepted new connection ("));
        Serial.print(itm->index);
        Serial.print(F(") from "));
        Serial.print(enetcli->remoteIP());
        Serial.print(':');
        Serial.println(enetcli->remotePort());
        Serial.println();
        Serial.flush();
       
        // print connected count
        console_print_connected_count();
       
        // return items index
        return itm->index;
      }
       
      void server_check_client_recv(void)
      {
        if (ClientList.size() == 0) return;
        ClientItem *itm;
        for (int idx = ClientList.size() - 1; idx >= 0; idx--)
        {
          itm = ClientList.get(idx);
          if (itm->client->connected()==0)
          {
            client_disconnected(itm);
            continue;
          }
          if (itm->client->available())
          {
            size_t rc = client_recv(itm);
            if (rc > 0)
            {
              // echo to Serial port
              Serial.print(F("Client #"));
              Serial.print(itm->index);
              Serial.print(F(" sent \""));
              Serial.print(itm->recv_buffer);
              Serial.println('\"');
              Serial.flush();
            }
            // rc will == 0 if nothing received
            // rc will == -1 if client entered "exit"
          }
          yield();
        }
      }
       
      size_t client_recv(ClientItem *cli)
      {
        int c;
        EthernetClientEx *cIn = (EthernetClientEx *)cli->client;
        cli->recv_buffer = "\0";
        cli->recv_cnt = 0;
        while ((c = cIn->read()) >= 0)
        {
          cli->recv_buffer += (char)c;
          cli->recv_cnt++;
        }
        // check to see if they typed "exit"
        if (test_for_cmd(String(F("exit")), cli->recv_buffer))
        {
          client_disconnecting(cli);
          delay(1); // give up timeslice
          cli->client->stop();
          Serial.print(F("called client->stop() for #")); Serial.println(cli->index); Serial.flush();
          return -1;
        }
        return cli->recv_cnt;
      }
       
      void client_disconnecting(ClientItem *cli)
      {
        Serial.print(F("Client #"));
        Serial.print(cli->index);
        Serial.println(F(" is disconnecting."));
        // send Goodbye Banner
        send_to(cli->index, String(F(GOODBYE_BANNER_STRING)));
        Serial.flush();
        delay(1);
        cli->client->stop();
      }
       
      void client_disconnected(ClientItem *cli)
      {
        // print notice on console
        Serial.print(F("Client #"));
        Serial.print(cli->index);
        Serial.print(F(" was connected for "));
        Serial.print(cli->elapsed() / 1000);
        Serial.print(F(" seconds, and has disconnected."));
        Serial.flush();
       
        // remove from list of clients
        ClientList.drop(cli->index);
        (*cli) = (*(ClientItem *)(unsigned long)0);
        // print connected count
        console_print_connected_count();
       
        // this causes seg fault ??
        //delete cli;
      }
       
      size_t send_to(int idx, const String &str)
      {
        return ClientList.send(str, idx);
      }
       
      size_t send_to_all(const String &str)
      {
        return ClientList.send2All(str);
      }
      
      

      Bingo ? Sounds good?

      Again, sorry my C++ knowledge is very limited...

      HTH

      posted in Troubleshooting
      soif
      soif