Communications among multiple Electrons

Hello,

I’m definitely not the first one to ask this question. So if anyone can point me to the right direction, that would be a great help. Is there anyway to let two or more Electrons talk to each other?

I did read the publish/subscribe contents in the documentation, but still feel that is NOT 100% fitting in my application. For example, if one Electron is sending the message out to another one to change the time on a clock or something and the receiver end wants to send a feedback to the transmitter, either confirm the job is done or failed. Is there a better method to implement this kind of communications between Electrons? If possible, any idea about what is the latency?

Thanks in advance.

What makes it 'unfitting'? You can build your example with publish/subscribe just fine. Latency depends on quite a few factors, and as such is hard to tell. There's a latency chart here though, if that makes any difference: http://status.particle.io

Thanks for your quick response.

the publish/subscribe contents, as I understand, it only pushes the info from TX to the RX, I don’t know how the RX can respond to the TX, which is important to me. In another word, just like the handshake in serial comm, Tx sends something to RX, within 500ms (just an example, could be any value), TX is expecting a feedback either success or failed or timeout. I don’t think the publish/subscribe is doing the same job here, maybe I am wrong. That’s the reason I mentioned it’s not 100% fitting here.

How about sending a publish back upon receiving one? Since it’s often not required, it’s not build in (to save on data as well), but there’s nothing stopping you from responding with another publish.

well, that’s the part I am more confused. Are you saying TX is publishing a value and the RX subscribe that one and based on it, RX publish a feedback value or something, then TX subscribe that feedback? If possible, do you know if there is an example somewhere?

That’s basically it.

Don’t know of the top of my hat about an example, but if you try creating a minimal version, we can go from there. Basically a ‘pong’ game of publishes. Photon 1 publishes, Photon 2 reacts to that, to which Photon 1 reacts, to which…

I have once done something similar for another member

// name all devices with a common prefix e.g ScruffyMaster, ScruffySlave, ...
#define COMMON_DEVICE_NAME_PREFIX "Scruffy"

/* Types required for RGB-LED control */
struct ARGB_COLOR {
	byte B;  // blue  channel
	byte G;  // green channel
	byte R;  // red   channel
	byte A;  // alpha channel to indicate RGB.control true (!= 0) / false (== 0)
};

union COLOR {
	unsigned int value;
	ARGB_COLOR argb;
};

struct devSetup 
{
    int   pinTrigger;
    int   pinAction;
    COLOR RGB;
    char  devName[32];
};

devSetup devices[] = 
{ 
    { -1, -1, 0x000000, "" }, 
    { D0, D7, 0xFF0000, "ScruffyRed"}, 
    { D1, D7, 0x00FF00, "Scruffy3G"}, 
    { D2, D7, 0x0000FF, "ScruffyBlue"}, 
    { D6, D7, 0xFFFFFF, "ScruffyWhite"} 
};

const int devCnt = sizeof(devices) / sizeof(devSetup);

char myName[32];
int  myIdx;

void sparkEvents(const char *topic, const char *data) 
{
    Serial.printlnf("%s: %s", topic, data);
    if (strstr(topic, "spark/device/name"))
    {
        strcpy(myName, data);
        Serial.printlnf("myName = %s", myName);
        // find my own index in devices list
        for (myIdx = 1; myIdx < devCnt && strcmp(myName, devices[myIdx].devName); myIdx++);
        myIdx %= devCnt;  // if I'm not in there reset to 0
    }
}

void devEvents(const char *topic, const char *data) 
{
    int idx;
    int state;
    
    Serial.printlnf("%s received %s: %s", myName, topic, data);

    if (strstr(topic, myName))
    {
        Serial.println("Uhh, That's me!");
        
        if (strstr(data, "=") && strstr(data, ";")) // rudimentary sanity check
        {
            idx = atoi(data);
            if (0 < idx && idx < devCnt)
            {
              state  = atoi(strstr(data, "=") + 1);
              if (state < 0) // negative numbers indicates toggle current state
                  state = !digitalRead(devices[idx].pinAction);
              digitalWrite(devices[idx].pinAction, state);
              Serial.printlnf("%s told me to %s its action", devices[idx].devName, state ? "START" : "STOP");
              if (devices[idx].RGB.value > 0)
                RGB.color(devices[idx].RGB.argb.R, devices[idx].RGB.argb.G, devices[idx].RGB.argb.B);
            }
            else
              Serial.println("I shan't talk to strangers!");
        }
    }
    else
        Serial.println("Meh, no one loves me!");
}

void setup() 
{
    Serial.begin(115200);

    for (int i=0; i < devCnt; i++)  // setup LED pins
    {
        pinMode(devices[i].pinTrigger, INPUT_PULLUP);
        pinMode(devices[i].pinAction, OUTPUT);
        if (devices[i].RGB.value > 0)
        {
          RGB.control(true);
          RGB.color(0,0,0);
        }
    }   
    
    Particle.subscribe("spark/", sparkEvents);
    Particle.subscribe(COMMON_DEVICE_NAME_PREFIX, devEvents, MY_DEVICES);

    Particle.publish("spark/device/name"); // push cloud to send my device name
}

void loop() 
{
    static uint32_t msLastPublish;
    int devNr;

    for (int i = 1; i < devCnt; i++)
    {
      if (!digitalRead(devices[i].pinTrigger))
      {
        Particle.publish(devices[i].devName, String::format("%d=%d;", myIdx, -1), PRIVATE); // -1 for toggle state (otherwise HIGH/LOW for explicit state)
        delay(1010); // currently there is an issue #1023 with the rate limit 
      }
    }
}

You have some buttons for your buddy devices you want to signal and on the receiver side the RGB LED indicates who called.
And then the receiver can either answer back or in turn invite another buddy. (or so :blush: it’s a while since)

1 Like

@Moors7 and @ScruffR

Thank both of you for your help. now have a basic idea and practice some coding. I haven’t done the feedback publish yet, it’s only oneway traffic for the message to send from Tx to Rx. I met a new problem. Please see details at below.

on the publish end, I have a particle.function(“setValue”, settingValue) and a particle.variable(“value”, &pricevalue, INT). One is for me can set a value on my iPhone App, the other is to show the value I set. then in the loop() I constantly check if the set value equals to the previous value, if yes, do nothing, if no, publish the new value.

Now I have another question, for now, my subscribe function is working. When I set up a value at the publish end, the subscriber will catch it and show it correctly. But only when both of the Electrons are online. If, for example, the subscriber is off-line and then get back online again, until the publish end updates a new value, the subscriber will not fetch the data from the particle cloud. how can I fix it, force the subscriber at lease fetch the cloud data at first start up?

Here is the pseudo code at the publish end,

    void setup()
    {
        Particle.function("SetPrice",settingValue);
        Particle.variable("CurrentPrice", &pricevalue, INT);
    }
    void loop()
    {
        delay(100);
    
        if(pricevalue != previousPriceValue)
        {
            Particle.publish("MY_UNIQUE_VALUE",String(pricevalue),60,PRIVATE);
            previousPriceValue = pricevalue;
        }
    }
    int settingValue(String price)
    {
        if(price != "")
        {
            char inputStr[64];
            price.toCharArray(inputStr, 64);
            pricevalue = atoi(inputStr);
        }
        else
        {
            pricevalue = -1;
        }
        return pricevalue;
    }

On the subscriber end,

    void Setup()
    {
        //for my serial usb debug
         Serial.begin(9600); 

         Particle.variable("GetValue_1", &pricevalue, INT); 
         if(Particle.subscribe("MY_UNIQUE_VALUE", myHandler, MY_DEVICES))
         {
             Serial.println("subscribe successfully!");
         }
         else
         {
             Serial.println("subscribe failed!");
         }
     }
    void loop()
    {

    }
    void myHandler(const char *event, const char *data)
    {
        Serial.print(event);
        Serial.print(", data: ");
        if (data)
        {
            Serial.println(data);
            pricevalue = atoi(data);
        }
        else
        {
            Serial.println("NULL");
        }    
    }

To tackle the offline problem you can have each device publish a briefMe! even for which all other devices subscribe and on such an event from their end publish their data for the newcomer to get updated.

That is a misconception. Currently the cloud doesn't hold any data from previous events or variable requests.

This is the old way to do this. It is no longer necessary to pass the type, or use "&"

Particle.variable("CurrentPrice", priceValue);

This is unnecessarily complex. You can use toInt() to convert a String object to an int directly.

priceValue = price.toInt();

@Ric

Yes, thank you. I originally run it on firmware v0.4.8, just upgrade to 0.6.0. All your suggestion did work.

@ScruffR

Thanks for correcting my misconception. Another question is for each publisher and subscriber, in order to solve the offline problem, each device should have a publish “briefMe” event? and the main publisher should subscribe all the subscribers’ “briefMe” event?

For example,

Electron 1(Publisher), subscribe event: briefMe1, briefMe2
Electron 2(Subscriber1), publish event: briefMe1
Electron 3(Subscriber2), publish event: briefMe2

What’s limitation about subscription? I remember to read about 4 handlers or something at somewhere. I guess I’m still confused about what you mentioned above.

I have provided you with an extensive sample using pub/sub which should give you a good starting point.
The first parameter of Particle.subscribe() is a prefix filter. So if you subscribe for briefMe you will get every event that starts with that term, no matter what follows after that prefix.

Inside the event handler you can then check the rest of the event name to distinguish where it came from (if you need).

So yes, there is a limit of 4 subscriptions, but as said, you only need one - no matter how many possible event sources trigger that event.


In my code I’m using a subscription for "spark/" to request the device name from the cloud. But once you got that sorted, you could even unsubscribe to free all four subscriptions if you actually needed all 4 for your own logic.

BTW: If you have multiple devices to run together, try to keep all your logic in an interchangable single code base wherever possible - that’ll save you a lot of headache in future.
My code above for example uses the device’s own name to distinguish what role it should play in the combined setup.
That way you could easily repurpose one device to take the role of the master, by just changing its name in the cloud and retrigger a name request - without the need to flash other code.

Is a cloud feature like this planned for the future as far as you know?

AFAIK at least event TTL was always planned and for variables it was an option.

1 Like