It sounds absolutely terrific ! :+1:
Thank you very much !
It sounds absolutely terrific ! :+1:
Thank you very much !
hi @Boots33
Thank you for your really helpful answer! :-D
Following your guidelines, I've succeeded to send MyMessages after registration is completed. Here is the code :
boolean executeOnce = false;
void setup(){ }
void loop(){
if ( ! executeOnce && isTransportReady() ) {
// code here to be executed once at startup
// ...
executeOnce = true;
}
}
Regarding 3), here is the behaviour I would like to build:
I've read the helpfull topic you have linked, and started to guess from reading the MyConfig.h : Is this simple code, the right one to act like I wish
#define MY_TRANSPORT_WAIT_READY_MS 5000
#define MY_TRANSPORT_SANITY_CHECK
// already set as default
//#define MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS (15*60*1000ul)
Obviously, in the loop, I always have to send MyMessages, with the ACK parameter set to false, to NOT wait for ACK replies whenever the GW is not available, right?
Cheers
In v2.0+ (I dont remember if it was the same in 1.5), there seems to be a sort of pseudo multitasking sequence happening in background when Mysensors.h is included in the sketch.
One of the consequence is that setup() function occurs at the "same" time as the Mysensor launch/presentation/registration process.
I discovered a before() function, which permits to be sure to launch instructions BEFORE launching MYS, but I did not discovered any after() function wich would allow me to send MyMessages only ONCE the init with the gateway has been completed :
I mean after the "init complete, id=xx, parent=0, distance=1, registration=1"
So the questions are :
What is the recommended way to launch (setup) code only ONCE the node is registered at the gateway, ie for sensing state of initial values. Is there some sort of after() function, or OnRegistered() function ?
Is there some documentation (expect reverse engineering the big MYS library) to understand all the steps that are happening in the background, when launching the library as a node.
It seems that since v2.0 , if the node is unable to find a gateway (ie the GW is too far, or offline) the sketch no longer launch... is there any mechanism to controls this behaviour (ie start anyway, being able to determine is the registration was successfull or not, and if not periodically try to register) ?
Thank you
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.
Did you still consider adding the mysensors.json file or some sort of file stucture standardization in the Github repos?
FYI I have some MySensors projects using the previous proposed standard :
https://github.com/soif/MySensors_ChristmasTree
https://github.com/soif/MySensors_MonitorsRelay
https://github.com/soif/MySensors_HSV_RGB_IR
and still
https://github.com/soif/MySensors_Template
https://github.com/soif/MySensors_Php_API
But it you finally don't mind (I guess) to use the proposed file structure or json files, I would like to make some clean-up in my repos...
The problem is not really concerning RGB to HSV or vice-versa (BTW if you have a working-well RGB to HSV code, i'm interested. I'm currently using the FastLED one ...)
The idea (and this is the main reason why HSV ever existed) is to get a easy and natural way to describe colors.
For anyone in the world it is easyer to say "I want a red, pale, not so bright" light, instead of a" #FFAA11" light! ;-) ;-)
When you are from a controller point of view, talking directly in HSV, allows to easily set a dim level (which is a major feature for a light, isn't it?), and a color (Hue). Devs like the Domoticz ones even dropped the Saturation at this time, but they are currently receiving (and could easily send) H(S)V values.
Also most (if not all) javascript color pickers are natively talking HSV.
About HSL vs HSV I think this not really important because it is just a story of different gamma correction / color space (if i understand well), and for devices intended as a Light (not as screen), color deviation is not a big issue. I just vote for the most used in related software (arduino lib + Javascripts color pickers), to try to match the requested color as accuratly as possible.
If you dont use git just replace the form.php with the one at GitHub
Let me know if it works flawlessly (eth or serial?).
cheers
Seems like Chrome + IE does not like function argument default values in JS, while mozilla does.
I've just pushed v1.21 which fixes this bug.
git pull ;-)
Updated to v1.2 adding Serial gateway experimental support!
As I don't have any serial gateway to test, I can't figure if it work or not, but I've implemented all the needed components to make it work.
Would you please test if it work fine for you, and push some PR if some things need to be fixed for the serial connection.
If you had to modify the "PhpSerial" class, would you please also push it back to the original developer repo
HTH
My GW don't crash while the second controller get a connection, it just throw away the previous connection.
I've tested this with MYS v1.51, 1.54 and 2.0beta.
My two controllers are :
You can check this by having one controller connected, then from a terminal console type :
telnet IP_OF_THE_GW 5003
It will launch a telnet session an you will see the GW messages scrolling, but when you send any characters (ie just hit return), boom, the first connected controller is immediately disconnected.
I'm not worried about any losses while converting HSV->RGB : in this direction, it work pretty well (whenever the software is well written). This is in the opposite direction that conversion is happening bad. (FastLed note about this)
I mean from the node, if I would like to work internally in HSV (in order to be able to support SMOOTH and easy dimmimg of any colors). Geting data from the controller in RGB, then converting in HSV is both approximative and adds some uneeded processing.
Being able to easely DIM any color (ie just changing HSV.v), is one of the main argument to use HSV against raw RGB. In HSV mode you can aslo easily fade between colors (just changing the hue). This is both easy, and really intuitive.
IMHOThese 2 features are importants to well suport RGB strips.
Doing this in HSV, is just a few lines of code (ie using FastLed) while doing this in RGB is a nightmare and absolutely not intuitive.
Anyway my test node works in both modes, but receiving HSV datas, would be a must and thus allow the controller so expose an HSV parameters in the API/script/GUI.
Just imagine creating a LUA script ie, in Domoticz (or any controller) to dim an RGB strip in raw RGB mode, vs doing this in HSV.... One is very complicated while the other is really easy. and dimming a RGB strip should be as simple as dimming a single color bulb, isnt it?
I'm proposing to add an S_HSV_LIGHT type for controlling Led Strips.
FYI : I already have developed an (almost finished) sketch/proof-of-concept that controls a RGB led strip from HSV messages , and/or RGB messages, using the FastLed library. Obviously, the HSV messages are more intuitives and precises than the RGB messages.
@gyro
Keep up the good work debugging this chinese box !
This box is a really good value for money box to build mysensors projects ( at least box + lipo + solar panel).
If your investigations let us to also use the built in Charger / PIR / LEDS , it would just be amazing.
Please keep us in touch ! ;-)
I've just posted version 1.1, including the form showed above. ;-)
Any current users ?
Comments?
BTW would you mind if I opened an issue at GH about this, linking to this topic, to help you and others developpers to have a reminder at the right place.
And maybe another C++ hero would see it, and start working on it! :-p
Currently Working (develop branch) on a convenient form to convert/send messages .

It should certainly help developers to test their sensors/gateway ;-)
Happy if i could have helped you to save you some time to find a way for implementing a solution.
Let us know when you'd made some progress ;-)
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
Thank you for poiting me to this part of the code. It immediately showed something interesting :
Your highlighted code is for ESP8266 but i'm using W5100 (the recommended one)...
If I read correctly the code just following for the W5100, I see that :
// W5100/ENC module does not have hasClient-method. We can only serve one client at the time.
Sorry my C++ knowledge is very limited...