Serial gateway, which example to follow??



  • Hi all, I am pretty new to Mysensor, however I have used some sensor code successfully.

    My setup is a RPI b+ running openhab connected to my gateway with usb cable which is an arduino due.

    So the plan is to use the openhab binding to connect tothe gateway. So my idea was that locally attached sensor will be assigned node id 0-9, softwareserial nodes 10-19 and rf everything higher.

    So I started reading up on the serial gateway, the one that comes in the sketch folder of the development branch, on the mysensor webpage, some on rpi. So I am unsure which one to use.. The one in the sketch folder, I dont know how to decode income serial message and send it off on the software serial, also it fails to compile when you disable RF(like if you only have locally attached sensors or software serial nodes)

    I also did some reading up on rs422 and software serial.

    So I have made a sketch that almost compiles, I only have an issue with using the MyMessage library to split the incoming string and were wondering if someone could help me out on how to use it so that i can redirect incoming nodeID 10 to software serial.

    #include <SoftwareSerial.h>
    #include <SPI.h>
    #include <MyConfig.h>
    #include <MySensors.h>
    
    
    
    // Enable debug prints to serial monitor
    //#define MY_DEBUG
    
    /*
    * Un comment the lines below whenever we want to use a radio modem..
    */
    // Enable and select radio type attached
    #define MY_RADIO_NRF24
    //#define MY_RADIO_RFM69
    //
    // Enable serial gateway
    #define MY_GATEWAY_SERIAL
    
    
    
    SoftwareSerial mySerial(10, 11); // RX, TX
    
    //Some constant that holds the things received on serial line
    #define MAX_RECEIVE_LENGTH 100 // Max buffersize needed for messages coming from controller
    #define MAX_SEND_LENGTH 120 // Max buffersize needed for messages destined for controller
    char inputString[MAX_RECEIVE_LENGTH] = "";    // A string to hold incoming commands from serial/ethernet interface
    int inputPos = 0;
    boolean commandComplete = false;  // whether the string is complete
    
    
    //Switch  Bar  "Bar"  <drink>   (gBar)  {mysensors="1;2;V_LIGHT"}
    void parseAndSend(char *commandBuffer) {
      MyMessage msg = new MyMessage(); // fails here.....
      
      msg.set(commandBuffer);
    
      //Locally attached sensor......
      if (msg.destination = 0) {
    
      }
    
      //We forward our message on software serial
      else if (msg.destination = 10) {
        mySerial.write(commandBuffer);
      }
    
      // send it out on the radio modem, to be implemented later
      else {
    
      }
    
    }
    
    
    
    void setup() {
      // Send startup log message on serial
      //serial(PSTR("0;0;%d;0;%d;Gateway startup complete.\n"),  C_INTERNAL, I_GATEWAY_READY);
      //Set up all our software serial ports...
      mySerial.begin(9600);
      // Setup locally attached sensors
    }
    
    void presentation() {
      // Present locally attached sensors
      //how to do this???
    }
    
    void loop() {
      // Send locally attached sensor data here
      //gw.process();
    
      if (commandComplete) {
        // A command wass issued from serial interface
        // We will now try to send it to the actuator
        parseAndSend(inputString);
        commandComplete = false;
        inputPos = 0;
      }
    
      if (mySerial.available())
        Serial.write(mySerial.read());
    }
    
    
    void serialEvent() {
      while (Serial.available()) {
        // get the new byte:
        char inChar = (char)Serial.read();
        // if the incoming character is a newline, set a flag
        // so the main loop can do something about it:
        if (inputPos < MAX_RECEIVE_LENGTH - 1 && !commandComplete) {
          if (inChar == '\n') {
            inputString[inputPos] = 0;
            commandComplete = true;
          } else {
            // add it to the inputString:
            inputString[inputPos] = inChar;
            inputPos++;
          }
        } else {
          // Incoming message too long. Throw away
          inputPos = 0;
        }
      }
    }
    
    

    Any help will be highly appreciated



  • @skatun said:

    MyMessage msg = new MyMessage(); // fails here.....

    msg.set(commandBuffer);

    It is basically this piece of the code I have problems with:)


  • Admin

    Not sure what you're doing here.. But if you want to receive data from the controller, just use the receive() function.. No need to parse any serial data. It's handled by MySensors.

    You just do exactly like any other radio node.. I.e. call present() etc,



  • @hek
    Thanks, so this link is not valid?

    so how can I use the receive method to obtain the nodeID?

    nodeID = receive().destination
    

  • Admin

    Not if you use 2.0, which the example above looked like.

    Check out the 2.0 RelayActuator-example for an hint of how to use receive() for incoming data.



  • @hek
    Thanks, I have downloaded today the 2.0 version from github
    This however gives me this error code:

    In file included from C:\Users\kim\Documents\Arduino\openhab\openhab.ino:4:0:
    
    C:\Users\kim\Documents\Arduino\libraries\MySensors/MySensors.h:287:4: error: #error No forward link or gateway feature activated. This means nowhere to send messages! Pretty pointless.
    
       #error No forward link or gateway feature activated. This means nowhere to send messages! Pretty pointless.
    
        ^
    

    Which I thought was patched now..

    I also tried to implement the receive code as you suggested, see code below in version 2.0 style and not 1.5 style as the webpage suggest. My gateway currently are pretty simply, if it receieve node id= 10 send it off on software serial, i.e send the whole buffer to that node. (Eventually I will have 7 software serial nodes..id =10..16 and 24 locally attached sensors, as well as a few radio nodes)

    #include <SoftwareSerial.h>
    #include <SPI.h>
    #include <MyConfig.h>
    #include <MySensors.h>
    
    // Enable debug prints to serial monitor
    //#define MY_DEBUG
    
    // Enable serial gateway
    #define MY_GATEWAY_SERIAL
    
    SoftwareSerial mySerial(10, 11); // RX, TX
    
    void setup() {
    
      //Set up all our software serial ports...
      mySerial.begin(9600);
      // Setup locally attached sensors
    }
    
    void presentation() {
      // Present locally attached sensors
    
    }
    
    void loop() {
      // Send locally attached sensor data here
      gw.process();
    
      //Just forward data received on software serial to the controller
      if (mySerial.available()) {
        Serial.write(mySerial.read());
      }
    
    }
    
    
    void receive(const MyMessage &msg) {
    
    
      //Locally attached sensor......
      if (msg.destination = 0) {
    
      }
    
      //We forward our message on software serial
      else if (msg.destination = 10) {
        mySerial.write(msg); //is this the complete message??
      }
    
      // send it out on the radio modem, to be implemented later
      else {
    
      }
    
    }
    

  • Admin

    Include the MySensor.h after #defines.

    Use one of the examples as a template. So you'll get the structuring right.



  • @hek
    Now it compiles:) Thanks. So 2 questions for you:
    1:The receieve event is triggered when a whole string with \n is received on the serial line?
    2: How to get the whole string sent from controller? msg.getString() or msg.getStream() (Trying to understand the MyMessage.h file...)

    Updated code below

    // Enable debug prints to serial monitor
    //#define MY_DEBUG
    
    // Enable serial gateway
    #define MY_GATEWAY_SERIAL
    
    
    #include <SoftwareSerial.h>
    #include <SPI.h>
    #include <MySensors.h>
    
    SoftwareSerial mySerial(10, 11); // RX, TX
    
    void setup() {
    
      //Set up all our software serial ports...
      mySerial.begin(9600);
      // Setup locally attached sensors
    }
    
    void presentation() {
      // Present locally attached sensors
      //how to do this???
    }
    
    void loop() {
      // Send locally attached sensor data here
    
      if (mySerial.available()) {
        Serial.write(mySerial.read());
      }
    
    }
    
    
    void receive(const MyMessage &msg) {
    
    
      //Locally attached sensor......
      if (msg.destination == 0) {
    
      }
    
      //We forward our message on software serial
      else if (msg.destination == 10) {
        //mySerial.write(msg); //is this the complete message??
      }
    
      // send it out on the radio modem, to be implemented later
      else {
    
      }
    
    }
    


  • @hek
    So finally I got home to grab my arduino so I can do some hardware testing and not just software!

    It seems like the receive method is not running on the gateway, I have read through everything on the library api three times now, but I still can not figure out how the receive method are working. It runs inside the main loop, hence you can not use any sleep routine as far as I understood.
    0_1468333270528_Screenshot_1.png image url)

    What can I do to get the receive method to run?



  • Its not something in the transport class I need to change right?


  • Hero Member

    @skatun What do you want to accomplish? The "receive method" just runs if you receive messages from other sensors (not locally connected}.



  • @AWI
    Basically I have my Gateway(Mega, might swap to Due, higher resolution ADC), on my gateway I have:
    1 radio modem connected,
    7 Arduino nano which are soldered to tx/rx(I might need to resolder those?),
    12 motion sensors,
    1 pulsePowerMeter,
    6 ledstrips
    1 IR generator(simulate my tv /stereo,etc remote)

    So if my controller Openhab:
    sends or receive CHildID = 0 then send/receive data to locally attached sensor
    sends or receive CHildID = 10-16 then send/receive data on software serial to the 7 arduino nano(Basically relay sensor nodes)
    sends or receive CHildID = 20-31 then send/receive data on radio to my radio nodes(temperature mainly)

    My problem is that I can not figure out how to set up software serial with my sensor.

     If msg.destination =16 sendTo software Serial on pin 34
    

    Below is a picture of my Gateway setup:
    0_1468414835666_IMG_20160713_145404[1].jpg


  • Hero Member

    @skatun I'm still confused. Looks like you are forcing the MySensors protocol to do your own thing.
    Your topology has different nano's (nodes) connected via tx/rx to the gateway. The MySensors way to communicate with these is to treat them as 'nodes' and communicate to them by means of the wired protocol. Each of these nodes needs a different node number.
    Then if you want to combine this with radio communication you need a different gateway which communicates by means of the wireless (rf24) protocol with the remote nodes.



  • @AWI

    Quote:"means of the wired protocol"
    I was so fortunate that I could connect most of my nodes and sensors wired, more reliable, no need to change battery so I tried todo so with most of my nodes and sensors.

    Quote : "Then if you want to combine this with radio communication you need a different gateway"
    Why do I need two gateways, why cant one gateway handle both wired and radio? For now I don't need the radio, just locally and wired connection

    Quote:" Each of these nodes needs a different node number."
    Yupp thats why I try to give them static node numbers: So the one on balcony gets ID 11 the one in the bar is 13 and so on. (my idea was to group ID numbers, 0 locally sensor, 10-19 software serial, >20 radio nodes, >50 wifi nodes ,>70 rs485 nodes)

    The benefit of using MySensor is that the Openhab has a binding for it, I can use the example codes for my nodes(like the relay with button sketch) and I need some format of the string sended/received from the controller to my nodes anyway, so why not use the mysensor format?

    so My orignally Idea was to use the serial event:

    void serialEvent() {
      while (Serial.available()) {
        // get the new byte:
        char inChar = (char)Serial.read();
        // if the incoming character is a newline, set a flag
        // so the main loop can do something about it:
        if (inputPos < MAX_RECEIVE_LENGTH - 1 && !commandComplete) {
          if (inChar == '\n') {
            inputString[inputPos] = 0;
            commandComplete = true;
          } else {
            // add it to the inputString:
            inputString[inputPos] = inChar;
            inputPos++;
          }
        } else {
          // Incoming message too long. Throw away
          inputPos = 0;
        }
      }
    }
    

    and then use the Message class to split the input buffer the string based "," like in vba I would just use arrayMy = split(inputBuffer,",")

    Then If ID=11 forward the whole thing to software serial to the balcony, then the code on that node would be exactly the same as if the message came over the radio link.

    That was my orginally idea, but then @hek suggested that I should rather use the receive method since this grab the buffer from serial somehow.

    I am open to all solutions:) I just have limited space on some of my nodes which makes it hard to implement a radio link on them as well as an rs485 shield. (even using nano is hard)



  • @hek
    I am making great progress on my gateway:) I am almost done wiring everything and software is coming along. So my sensors sends out data nicely to the controller, however I can not receive commands, which is essentials for my RGB led strips.

    I am trying to send this from the Serial Monitor in arduino: 0,30,1,0,40,ff0000\n

    However the received method never gets called, what am I missing?

    // Enable debug prints to serial monitor
    //#define MY_DEBUG
    
    // Enable serial gateway
    #define MY_GATEWAY_SERIAL
    
    
    #include <SoftwareSerial.h>
    #include <SPI.h>
    #include <MySensors.h>
    #include <ChainableLED.h>
    
    SoftwareSerial mySerial(10, 11); // RX, TX
    
    
    MyMessage msg1(1, V_LIGHT);
    
    //RGB
    int NUM_LEDS = 34;
    ChainableLED leds(5, 6, NUM_LEDS);  // CLK, DATA, LEDS
    
    //PowerMeter
    #define DIGITAL_INPUT_SENSOR 3  // The digital input you attached your light sensor.  (Only 2 and 3 generates interrupt!)
    #define PULSE_FACTOR 1000       // Nummber of blinks per KWH of your meeter
    #define SLEEP_MODE false        // Watt-value can only be reported when sleep mode is false.
    #define MAX_WATT 10000          // Max watt value to report. This filetrs outliers.
    
    
    #define CHILD_ID_POWER 2       // Id of the sensor child
    #define CHILD_ID_RGB_START 30
    #define NUM_LEDS_STRIPS 5
    
    
    int counter[NUM_LEDS_STRIPS];
    int current[NUM_LEDS_STRIPS][3];
    int step[NUM_LEDS_STRIPS][3];
    
    
    unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in milliseconds). We don't wnat to spam the gateway.
    double ppwh = ((double)PULSE_FACTOR) / 1000; // Pulses per watt hour
    boolean pcReceived = false;
    volatile unsigned long pulseCount = 0;
    volatile unsigned long lastBlink = 0;
    volatile unsigned long watt = 0;
    unsigned long oldPulseCount = 0;
    unsigned long oldWatt = 0;
    double oldKwh;
    unsigned long lastSend;
    MyMessage wattMsg(CHILD_ID_POWER, V_WATT);
    MyMessage kwhMsg(CHILD_ID_POWER, V_KWH);
    MyMessage pcMsg(CHILD_ID_POWER, V_VAR1);
    
    
    void setup() {
    
      //Set up all our software serial ports...
      mySerial.begin(9600);
      // Setup locally attached sensors
    
    
      //RGB
    
      leds.init();
      for (byte CHILD_ID_RGB = CHILD_ID_RGB_START; CHILD_ID_RGB < NUM_LEDS_STRIPS; CHILD_ID_RGB++) {
        counter[CHILD_ID_RGB] = 0;
        current[CHILD_ID_RGB][0] = 0;
        current[CHILD_ID_RGB][1] = 0;
        current[CHILD_ID_RGB][2] = 0;
        step[CHILD_ID_RGB][0] = 0;
        step[CHILD_ID_RGB][1] = 0;
        step[CHILD_ID_RGB][2] = 0;
        leds.setColorRGB(CHILD_ID_RGB, current[CHILD_ID_RGB][0], current[CHILD_ID_RGB][1], current[CHILD_ID_RGB][2]); // Turn of on startup
      }
    
    
      // Power Meter
      // Fetch last known pulse count value from gw
      request(CHILD_ID_POWER, V_VAR1);
    
      // Use the internal pullup to be able to hook up this sketch directly to an energy meter with S0 output
      // If no pullup is used, the reported usage will be too high because of the floating pin
      pinMode(DIGITAL_INPUT_SENSOR, INPUT_PULLUP);
    
      attachInterrupt(digitalPinToInterrupt(DIGITAL_INPUT_SENSOR), onPulse, RISING);
      lastSend = millis();
    
    }
    
    void presentation() {
      // Present locally attached sensors
    
      //RGB 0,30,1,0,40,ff0000\n
      sendSketchInfo("RGB Led", "1.2");
      for (byte CHILD_ID_RGB = CHILD_ID_RGB_START; CHILD_ID_RGB < NUM_LEDS_STRIPS; CHILD_ID_RGB++) {
        present(CHILD_ID_RGB, S_RGB_LIGHT);
      }
    
    
      //RELAY
      sendSketchInfo("Relay & Button", "1.0");
    
      // Register all sensors to gw (they will be created as child devices)
      present(1, S_LIGHT);
    
    
      //POWER METER
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo("Energy Meter", "1.0");
    
      // Register this device as power sensor
      present(CHILD_ID_POWER, S_POWER);
    
    
    }
    
    void loop() {
      // Send locally attached sensor data here
      //send(msg1.set(true), true); // Send new state and request ack back
      if (mySerial.available()) {
        Serial.write(mySerial.read());
      }
    
      //RGB
      for (byte CHILD_ID_RGB = CHILD_ID_RGB_START; CHILD_ID_RGB < NUM_LEDS_STRIPS; CHILD_ID_RGB++) {
        if (counter[CHILD_ID_RGB] >= 0) {
          counter[CHILD_ID_RGB]--;
          int i = 1020 - counter[CHILD_ID_RGB];
          current[CHILD_ID_RGB][0] = calculateVal(step[CHILD_ID_RGB][0], current[CHILD_ID_RGB][0], i);
          current[CHILD_ID_RGB][1] = calculateVal(step[CHILD_ID_RGB][1], current[CHILD_ID_RGB][1], i);
          current[CHILD_ID_RGB][2] = calculateVal(step[CHILD_ID_RGB][2], current[CHILD_ID_RGB][2], i);
    
          leds.setColorRGB(CHILD_ID_RGB, current[CHILD_ID_RGB][0], current[CHILD_ID_RGB][1], current[CHILD_ID_RGB][2]);
        }
      }
    
      //POWER
      unsigned long now = millis();
      // Only send values at a maximum frequency or woken up from sleep
      bool sendTime = now - lastSend > SEND_FREQUENCY;
      if (pcReceived && (SLEEP_MODE || sendTime)) {
        // New watt value has been calculated
        if (!SLEEP_MODE && watt != oldWatt) {
          // Check that we dont get unresonable large watt value.
          // could hapen when long wraps or false interrupt triggered
          if (watt < ((unsigned long)MAX_WATT)) {
            send(wattMsg.set(watt));  // Send watt value to gw
          }
          Serial.print("Watt:");
          Serial.println(watt);
          oldWatt = watt;
        }
    
        // Pulse cout has changed
        if (pulseCount != oldPulseCount) {
          send(pcMsg.set(pulseCount));  // Send pulse count value to gw
          Serial.print("PulseCount:");
          Serial.println(pulseCount);
          double kwh = ((double)pulseCount / ((double)PULSE_FACTOR));
          oldPulseCount = pulseCount;
          if (kwh != oldKwh) {
            send(kwhMsg.set(kwh, 4));  // Send kwh value to gw
            oldKwh = kwh;
            Serial.print("KWh:");
            Serial.println(kwh);
          }
        }
        lastSend = now;
      } else if (sendTime && !pcReceived) {
        // No count received. Try requesting it again
        request(CHILD_ID_POWER, V_VAR1);
        Serial.println("requesting a new value");
        lastSend = now;
      }
    
    }
    
    
    
    //PowerMeter
    void onPulse() {
      unsigned long newBlink = micros();
      unsigned long interval = newBlink - lastBlink;
      if (interval < 10000L) { // Sometimes we get interrupt on RISING
        return;
      }
      watt = (3600000000.0 / interval) / ppwh;
      lastBlink = newBlink;
    
      pulseCount++;
    }
    
    //RGB
    void setColor(byte led, int R, int G, int B) {
      step[led][0] = calculateStep(current[led][0], R);
      step[led][1] = calculateStep(current[led][1], G);
      step[led][2] = calculateStep(current[led][2], B);
    
      counter[led] = 255;
    }
    
    int calculateStep(int prevValue, int endValue) {
      int step = endValue - prevValue; // What's the overall gap?
      if (step) {                      // If its non-zero,
        step = 255 / step;            //   divide by 1020
      }
      return step;
    }
    
    int calculateVal(int step, int val, int i) {
    
      if ((step) && i % step == 0) { // If step is non-zero and its time to change a value,
        if (step > 0) {              //   increment the value if step is positive...
          val += 1;
        }
        else if (step < 0) {         //   ...or decrement it if step is negative
          val -= 1;
        }
      }
      // Defensive driving: make sure val stays in the range 0-255
      if (val > 255) {
        val = 255;
      }
      else if (val < 0) {
        val = 0;
      }
      return val;
    }
    
    
    
    
    
    void receive(const MyMessage & msg) {
      Serial.println("Hello");
    
      //send(msg1.set(true), true);
    
      if (msg.type == V_RGB) {
        String hexstring = msg.getString();
        long number = (long) strtol( &hexstring[0], NULL, 16);
        int colorR = number >> 16;
        int colorG = number >> 8 & 0xFF;
        int colorB = number & 0xFF;
    
        // Write some debug info
        Serial.print("Incoming change for sensor:");
        Serial.print(msg.sensor);
        Serial.print(", Red: ");
        Serial.print(colorR);
        Serial.print(", Green: ");
        Serial.print(colorG);
        Serial.print(", Blue: ");
        Serial.print(colorB);
        Serial.print(", New status: ");
        Serial.println(msg.getString());
    
        setColor(msg.sensor, colorR, colorG, colorB);
        //leds.setColorRGB(message.sensor, colorR, colorG, colorB);
      }
    
      //POWER
      if (msg.type == V_VAR1) {
        pulseCount = oldPulseCount = msg.getLong();
        Serial.print("Received last pulse count from gw:");
        Serial.println(pulseCount);
        pcReceived = true;
      }
    
      //Locally attached sensor......
      if (msg.destination == 0) {
    
      }
    
      //We forward our message on software serial
      else if (msg.destination == 10) {
        Serial.println("halla 3");
        //mySerial.write(msg.getStream()); //is this the complete message??
      }
    
      // send it out on the radio modem, to be implemented later
      else {
    
      }
    
    }
    

  • Admin

    @skatun GW receive() has been fixed in PR#522. We are currently working on the 2.0.1 release, more will follow soon.



  • @tekka Thanks for the feedback, does that implies if I download the dev branch from git now it should be fixed?

    here is the output from my serial monitor when i enabled logging:

    0;255;3;0;9;Starting gateway (R-NGA-, 2.0.0-beta)
    0;255;3;0;14;Gateway startup complete.
    0;2;2;0;24;
    0;255;3;0;9;No registration required
    0;255;3;0;9;Init complete, id=0, parent=0, distance=0, registration=1
    0;2;2;0;24;
    

    I am trying to get my RGB lights to work:

    
    #define CHILD_ID_RGB_START 30
    #define NUM_LEDS_STRIPS 5
    
    void presentation() {
      // Present locally attached sensors
    
      //RGB 0,30,1,0,40,ff0000\n
      sendSketchInfo("RGB Led", "1.2");
      Serial.println("Lets present some RGB");
      for (byte CHILD_ID_RGB = CHILD_ID_RGB_START; CHILD_ID_RGB < NUM_LEDS_STRIPS; CHILD_ID_RGB++) {
        Serial.print("RGB ID: ");
        Serial.println(CHILD_ID_RGB);
        present(CHILD_ID_RGB, S_RGB_LIGHT);
      }
    

    How can I add logging to Serial monitor to gateway, because Serial.println("Lets present some RGB"); does not produce any output?

    shouldn't this line registered my lights as sensors in MYSController?

    
    present(CHILD_ID_RGB, S_RGB_LIGHT);
    

    So I am afraid there is something wrong with my for loop, even though i can not spot the mistake.


  • Admin

    @skatun looks like you didn't define a radio on your GW, so your RGB example runs locally on the GW?



  • @tekka Thats correct, i am only running locally attached sensor:) I was luckily enough to run sth like 35 cables to my gateway, I might add radio in the future to reach down to my basement.


Log in to reply
 

Looks like your connection to MySensors Forum was lost, please wait while we try to reconnect.