Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
soifS

soif

@soif
Plugin Developer
About
Posts
36
Topics
6
Shares
0
Groups
1
Followers
0
Following
0

Posts

Recent Best Controversial

  • MySensors Php API class + command line script + Form
    soifS soif

    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 :-p

    My Project

  • RFC : Lazy Submission to OpenHardware.io using GitHub
    soifS soif

    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

    Feature Requests

  • MySensors Php API class + command line script + Form
    soifS soif

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

    Form

    It should certainly help developers to test their sensors/gateway ;-)

    My Project

  • RFC : Lazy Submission to OpenHardware.io using GitHub
    soifS soif

    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.

    Feature Requests

  • Other Controller get disconnected when sending any command to the Eth. Gateway (v2.0)
    soifS soif

    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 ! :-)

    Troubleshooting

  • Other Controller get disconnected when sending any command to the Eth. Gateway (v2.0)
    soifS soif

    Yep, comments are often useful when we are not that lazy to include them :-D

    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

    Troubleshooting
  • Login

  • Don't have an account? Register

  • Login or register to search.
  • First post
    Last post
0
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular