Trouble handling subscribe data

I’m giving Internet Buttons to all my brothers. I’m building an option where we can call each other to play a game of Overwatch. When someone publishes a reply, I’d like the Particle.subscribe to trigger the “overwatch” method. If any of my devices presses the “yes” button, all devices get a single green dot. If the press the “no” button, they all get a red dot. I think the data is being published, but all I get is an occasional “yes” dot no matter what I press.

Any suggestions?

Here’s my code:

1 Like

Do you really need a webhook/IFTTT?
Publish/subscribe between Particle devices works quite nicely and is easier to debug.

Okay, I think I’ve found a solution. Since I don’t know how to interpret the data coming in from a Particle.subscribe, (for example, why is it a const char* and not just a String?) I simply set two different webhooks. The result is simply based on what handler you trigger. If there is an easier way of doing it using publish/subscribe that doesn’t use a webhook, it’s a mystery to me.

Actually I prefere C strings (char[] & char*) over String objects - especially on micro controlers.

But you can always copy the const char* into a String object like this


void handler(const char* eventName, const char* data)
{
  String s1 = String(data);
  // or
  String s2 = data;
  // or even
  String s3(data); // my prefered way ;-)
  ...
}

But knowing C string functions like strstr(), strcmp(), `strtok() and such will always be beneficial when coding in C/C++.

Don’t let yourself be scared of to go down that road. It’s no mystery whatsoever.
Got a question or hit a brick wall, come ask :wink:

1 Like

You’re great, thanks. And maybe you can help get me unstuck. The webhook is tedious and limiting. I’m trying the approach below using only .variable/.publish/.subscribe. Here’s what I’m TRYING to make happen:

  1. The Internet Buttons are being used for IoT stuff around the house when someone .publishes their interest in playing a game

  2. All 6 Internet Buttons around the country are triggered to clear their display because one person published and everyone is subscribed

  3. Based on some part of the subscribed variable, we can see who triggered the event: b.ledOn(playerID, 0, 255, 0)

  4. Everyone can reply with Yes/No/Soon. Their reply lights up a specific LED for each brother of mine. The light is color-coded, {yes=green, no=red, soon=blue}

       #include "InternetButton/InternetButton.h"
        InternetButton b = InternetButton();
        const char *playerID = "1";
        
        void setup() {
        
            Particle.variable("overwatch", playerID);
            Particle.subscribe("overwatch", overwatchHandler);
        
            b.begin();
        }
        
        void loop(){
            if(b.buttonOn(3)){
                Particle.publish("overwatch", playerID)
                delay(500);
            }
        } //closes main loop
        
        void overwatchHandler(const char *event, const char *data){
            parsedData = int(data);
            b.ledOn(parsedData, 255, 255, 255);
        }

Just one note. In order to not violate the rate limit for publishes, you might eiter want to increase your delay() to 1010 or add some timeout that for one only allows one publish per second and also prevents multiple events firing if you keep the button pressed.

Like this

#include "InternetButton/InternetButton.h"
InternetButton b = InternetButton();
const char evtPrefix[] = "Player_";
const int playerID = 1;

void setup() 
{
  Particle.variable("PlayerID", playerID);
  Particle.subscribe(evtPrefix, overwatchHandler);
  b.begin();
}

void loop()
{
  static uint32_t ms;       // to keep track of last publishing time
  static int lastState;     // to keep track of the last button state
  static bool needPublish;  // keep track of pending publish requests
  
  int actState = b.buttonOn(3); 

  if(actState && actState != lastState)
  {                         // only if the button is pressed first
    needPublish = true;     // inidcate a pending publish
  }

  if (needPublish && millis() - ms > 1010)
  {                         // if there is a pending publish request and  
    char evt[32];
    ms = millis();          // the last publish is long enough gone
                            // try to publish
    
    snprintf(evt, sizeof(evt), "%s%d", evtPrefix, playerID);
    needPublish = !Particle.publish(evt, "Wanna play?");
                            // but if it failed keep it pending (needPublish = !success)  
  }

  lastState = actState;
}

void overwatchHandler(const char *event, const char *data)
{
  if (!strcmp(data, "Wanna play?")) // string compare if they are the same returns 0, so invert
  {
    int player = atoi(&event[strlen(evtPrefix)]);
    b.ledOn(player, 255, 255, 255);
  }
  else // here you could parse the responses from the other buttons via data
  {
    ...
  }  
}

For reference
http://www.cplusplus.com/reference/cstdio/snprintf/
http://www.cplusplus.com/reference/cstring/strcmp/
http://www.cplusplus.com/reference/cstdlib/atoi/


For another member I came up with this piece of code to have several Photons communicate with each ohter with just one code to flash to all of them.
I guess this could be adapted for your use too.
You can use this code on your InternetButtons too, you’d just need to name them as indicated in the code

// name all devices with a common prefix 
#define COMMON_DEVICE_NAME_PREFIX "Player_"

/* 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, "" }, 
    { D4, D1, 0xFF0000, "Player_1"}, 
    { D5, D2, 0x00FF00, "Player_2"}, 
    { D6, D3, 0x0000FF, "Player_3"}, 
    { D7, A1, 0xFFFFFF, "Player_4"} 
};

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 
      }
    }
}
1 Like

Wow! Again, thanks for holding my hand through this project. If I can trouble you for more help, I’ve got a whole bunch of questions. First of all, I get an odd compile error when I check the code. Why on Earth is the library all-the-sudden not found?: http://screencast.com/t/7e6Shd4ltE

The rest of my questions (6) have been added as comments. Please don’t let me take too much of your time.

//The lib isn't loading!
#include "InternetButton/InternetButton.h"
InternetButton b = InternetButton();

//This goes in front of all players
const char evtPrefix[] = "Player_";

//This changes based on who has the device
const int playerID = 1;

void setup() 
{
    //?1?Why do we need to register this variable? It won't change. 
    Particle.variable("PlayerID", playerID);
    //?2?Why subscribe to evtPrefix? isn't that just a const used during publish?
    Particle.subscribe(evtPrefix, overwatchHandler);
    b.begin();
}

void loop()
{
    //--------------------------------
    //------AVOID PUBLISH SPAM--------
    //--------------------------------
    static uint32_t ms;       // to keep track of last publishing time
    static int lastState;     // to keep track of the last button state
    static bool needPublish;  // keep track of pending publish requests
  
    //Why is this an int? Shouldn't this be a bool? If buttonOn(3) is true or not?
    int actState = b.buttonOn(3); 

    //If the button is pressed and it's not the, uhh, same press as last time?
    if(actState && actState != lastState)
    {                         // only if the button is pressed first
        needPublish = true;     // inidcate a pending publish
    }
    //If we need to publish and it isn't to soon after last time
    if (needPublish && millis() - ms > 1010)
    {                         // if there is a pending publish request and  
        char evt[32];
        ms = millis();          // the last publish is long enough gone
                            // try to publish
    
        //Isn't printf an output for a command line? Where is it printing?
        snprintf(evt, sizeof(evt), "%s%d", evtPrefix, playerID);
        //?3? I think this is clever, but why publish "evt?" Doesn't need to match the subscribe?
        needPublish = !Particle.publish(evt, "Wanna play?");
                            // but if it failed keep it pending (needPublish = !success)  
  }

  lastState = actState;
}

void overwatchHandler(const char *event, const char *data)
{
    //?4? Doesn't this assume any response that isn't an invite is a positive RSVP?
    if (!strcmp(data, "Wanna play?"))  // string compare if they are the same returns 0, so invert
    {
        //?5? I think this converts the last char, or rather anything after the prefix into an int?  
        int player = atoi(data[strlen(evtPrefix)]);
        b.ledOn(player, 255, 255, 255);
    }
    else // here you could parse the responses from the other buttons via data
    {
        //?6? Do you mean something like this? 
        if(strcmp(data, "Sure!"))
        {
            
        }
    }  
}

Have you imported the library via the library drawer (bookmark symbol)

I've just added it there because you had it in your original code

You subscribe to any event that starts with that prefix. So when you publish with prefixSome and prefixOther you will get the handler triggered for both events and can use the full event name to distinguish which event exactly was sent (or by who in your case)

bool takes the same four byte in memory as int and actually the return value of digitalRead() (the function used to retrieve the state) is int (0/1 represented as defined LOW/HIGH) not bool, but 0 and "not 0" will also evaluate to false/true.

What is not clear here?

That would be answered in the link I added. It's not printf() but sprintf()/snprintf() which work on buffers - that's where it's printing to - into the buffer.

see above for prefix subscribe

Nope, only that it's a response and not an invite. Filtering the response type is up to the else branch.

Exactly, and as shown above that identifies who sent the event.

For example - this is one way to do the response filtering mentioned above.

1 Like

Okay, I tried walking away from this, chagrined and cursing C++. But my brothers have already bought the darn Internet Buttons on my recommendation, so I’m back and slowly trying to overcome by woeful ignorance. And there’s progress (much thanks to you). I think I’ve got my issues down to just this one:

Here’s my current code for reference:

// This #include statement was automatically added by the Particle IDE.
#include "InternetButton/InternetButton.h"
InternetButton b = InternetButton();
//This goes in front of all players
const char evtPrefix[] = "InvteFrmPlayer_";

//This changes based on who has the device
const int playerID = 1;

void setup() 
{

    //We subscribe to evtPrefix so any event start 
    Particle.subscribe(evtPrefix, inviteHandler);
    b.begin();
}

void loop() {

    //--------------------------------
    //------AVOID PUBLISH SPAM--------
    //--------------------------------
    static uint32_t ms;       // to keep track of last publishing time
    static int lastState;     // to keep track of the last button state
    static bool needPublish;  // keep track of pending publish requests
  
    //An int that will be used much like a boolean
    int actState = b.buttonOn(3); 

    //If the button is pressed and it's not the, uhh, same press as last time?
    if(actState && actState != lastState)
    {                         // only if the button is pressed first
        needPublish = true;     // inidcate a pending publish
    }
    //If we need to publish and it isn't to soon after last time
    if (needPublish && millis() - ms > 1010)
    {                         // if there is a pending publish request and  
        char evt[32];
        ms = millis();          // the last publish is long enough gone
                            // try to publish
    
        //Print what we're doing into the buffer
        snprintf(evt, sizeof(evt), "%s%d", evtPrefix, playerID);
        //We'll publish evt which will still get flagged by the subscribe because of the prefix
        needPublish = !Particle.publish(evt, "Wanna play?");
                            // but if it failed keep it pending (needPublish = !success) 
    }
    lastState = actState;
}

void inviteHandler(const char *event, const char *data)
{
    //std::string s = event;
    //int player = atoi(s[strlen(evtPrefix)]);
    //int player = atoi(event[strlen(evtPrefix)]);
}

Why the frustration?
You seem to be on a good way. What do they say about Rome being built in one day?

I'll look at your video and give you some hints. To start with const char* wants to be an address where to find a char while char is this character itself, so that's the reason for your first error.
But again, forget std::string too. Just go with the basic C strings :wink:

Once you manage these you can move on to tackle the bigger brothers of it.

So for your first problem, if you wrote this instead it would have built

void inviteHandler(const char *event, const char *data)
{
    std::string s = event;
    int player1 = atoi(&s[strlen(evtPrefix)]);    // mark the ampersand & denoting that you want the address, not the char
    int player2 = atoi(&event[strlen(evtPrefix)]);
}

Sorry, I missed that ampersand & in my code at first, but then added it obviously after you took a copy of my code :blush:
And you are right about event vs data :+1: (corrected it above just now)

I appreciate the encouragement. The event is finally posting! …but just the first time. I have to reboot in order for the event to trigger again. If you still have the patience to continue your excellent help, here’s the explanation and the code: http://screencast-o-matic.com/watch/cD1voOiujV

Code:

    // This #include statement was automatically added by the Particle IDE.
    #include "InternetButton/InternetButton.h"
    InternetButton b = InternetButton();
    //This goes in front of all players as the hook for our Particle.subscribe
    const char evtPrefix[] = "InvteFrmPlayer_";
    
    //This changes based on who has the device
    const int playerID = 1;
    
    //this helps the mode and mode animations
    int mode = 1;
    boolean modeReady = true;
    boolean showMode = true;
    int brightness = 1;
    
    void setup() 
    {
    
        //We subscribe to evtPrefix so any event that starts with it will trigger 
        Particle.subscribe(evtPrefix, inviteHandler);
        //not exactly sure why we need the begin, but I'm told it's necessary
        b.begin();
    }
    
    void loop() {
    
        //--------------------------------
        //------BUTTERY MODE SELECTOR-----
        //--------------------------------
        static uint32_t modeDelay;       // to keep track of last publishing time
        if(b.buttonOn(1) && millis() - modeDelay > 250){
            if(mode < 11){
                mode++;
            }
            else{
                mode = 1;
            }
            modeDelay = millis(); 
            showMode = true;
            b.allLedsOff();
        }
        
        if(showMode){
            b.ledOn(mode, 85, 85, 85);
            animateMode();
        } //else... start a timer to revert back to mode 1?
        
        switch (mode)
        {
            case 0:
                if(b.buttonOn(2)){
                    publisher("Sure!");
                }
                else if(b.buttonOn(3)){
                    publisher("Soon");
                }
                else if(b.buttonOn(4)){
                    publisher("Can't");
                }
                break;
            case 1:
                if(b.buttonOn(2) || b.buttonOn(3) || b.buttonOn(4)){
                    publisher("Wanna play?");
                }
                break;
            case 2:
                if(b.buttonOn(2)){
                    publisher("Mode2Action1");
                }
                else if(b.buttonOn(3)){
                    publisher("Mode2Action2");
                }
                else if(b.buttonOn(4)){
                    publisher("Mode2Action3");
                }
                break;
            case 3:
                if(b.buttonOn(2)){
                    publisher("Mode3Action1");
                }
                else if(b.buttonOn(3)){
                    publisher("Mode3Action2");
                }
                else if(b.buttonOn(4)){
                    publisher("Mode3Action3");
                }
                break;
            case 4:
                if(b.buttonOn(2)){
                    publisher("Mode4Action1");
                }
                else if(b.buttonOn(3)){
                    publisher("Mode4Action2");
                }
                else if(b.buttonOn(4)){
                    publisher("Mode4Action3");
                }
                break;
            case 5:
                if(b.buttonOn(2)){
                    publisher("Mode5Action1");
                }
                else if(b.buttonOn(3)){
                    publisher("Mode5Action2");
                }
                else if(b.buttonOn(4)){
                    publisher("Mode5Action3");
                }
                break;
            case 6:
                if(b.buttonOn(2)){
                    publisher("Mode6Action1");
                }
                else if(b.buttonOn(3)){
                    publisher("Mode6Action2");
                }
                else if(b.buttonOn(4)){
                    publisher("Mode6Action3");
                }
                break;
            case 7:
                if(b.buttonOn(2)){
                    publisher("Mode7Action1");
                }
                else if(b.buttonOn(3)){
                    publisher("Mode7Action2");
                }
                else if(b.buttonOn(4)){
                    publisher("Mode7Action3");
                }
                break;
            case 8:
                if(b.buttonOn(2)){
                    publisher("Mode8Action1");
                }
                else if(b.buttonOn(3)){
                    publisher("Mode8Action2");
                }
                else if(b.buttonOn(4)){
                    publisher("Mode8Action3");
                }
                break;
            case 9:
                if(b.buttonOn(2)){
                    publisher("Mode9Action1");
                }
                else if(b.buttonOn(3)){
                    publisher("Mode9Action2");
                }
                else if(b.buttonOn(4)){
                    publisher("Mode9Action3");
                }
                break;
            case 10:
                if(b.buttonOn(2)){
                    publisher("Mode10Action1");
                }
                else if(b.buttonOn(3)){
                    publisher("Mode10Action2");
                }
                else if(b.buttonOn(4)){
                    publisher("Mode10Action3");
                }
                break;
            case 11:
                if(b.buttonOn(2)){
                    publisher("Mode11Action1");
                }
                else if(b.buttonOn(3)){
                    publisher("Mode11Action2");
                }
                else if(b.buttonOn(4)){
                    publisher("Mode11Action3");
                }
                break;
            
        }//closes switch
        
    
    }//close loop()
    
    
    //--------------------------------
    //------IFTTT and HANDLERS--------
    //--------------------------------
    
    void publisher(const char *msg){
    
        static uint32_t ms;       // to keep track of last publishing time
        static bool needPublish = true;  // keep track of pending publish requests
      
        //CUT: static int lastState;     // to keep track of the last button state
        //CUT: An int that will be used much like a boolean
        //CUT: int actState = b.buttonOn(3); 
        //CUT: if(actState && actState != lastState){
            //CUT: needPublish = true;}   // inidcate a pending publish
    
        while(needPublish)
        {
            if (needPublish && millis() - ms > 1010)
            {                         // if there is a pending publish request and  
                ms = millis();          // the last publish is long enough gone
                                // try to publish
        
                if(mode == 1 || mode == 0){
                    char evt[32];
                    //Print what we're doing into the buffer
                    snprintf(evt, sizeof(evt), "%s%d", evtPrefix, playerID);
                    //We'll publish evt which will still get flagged by the subscribe because of the prefix
                    // but if it failed keep it pending (needPublish = !success) 
                    needPublish = !Particle.publish(evt, msg);
                }else{
                    needPublish = !Particle.publish(msg);
                }
                                
            }
        } //closes while loop
        //CUT: lastState = actState;
    }//closes publisher
    
    
    void inviteHandler(const char *event, const char *data)
    {
        if(showMode){
            mode = 0;
            b.allLedsOff();  
            showMode = false;
            //TODO: start a timer to switch off of listen mode
        }
        int player = atoi(&event[strlen(evtPrefix)]);
        if (!strcmp(data, "Wanna play?") || !strcmp(data, "Sure!")){
            b.ledOn(player, 0, 255, 0);
        }
        else if(!strcmp(data, "Soon")){
            b.ledOn(player, 0, 0, 255);
        }
        else if(!strcmp(data, "Can't")){
            b.ledOn(player, 255, 0, 0);
        }
    }
    
    
    //--------------------------------
    //------ANIMATIONS----------------
    //--------------------------------
    void animateMode()
    {
        static uint32_t fadeDelay;       // to keep track of last publishing time
        if(millis() - fadeDelay > 550)
        {
            switch (mode)
            {
            case 1:
                for(int x = 1; x < 12; x++){
                    if(x != mode){
                        b.ledOn(x, 21*brightness, 0*brightness, 0*brightness);
                    }
                }
                break;
            case 2:
                for(int x = 1; x < 12; x++){
                    if(x != mode){
                        b.ledOn(x, 0*brightness, 21*brightness, 0*brightness);
                    }
                }
                break;
            case 3:
                for(int x = 1; x < 12; x++){
                    if(x != mode){
                        b.ledOn(x, 0*brightness, 0*brightness, 21*brightness);
                    }
                }
                break;
            case 4:
                for(int x = 1; x < 12; x++){
                    if(x != mode){
                        b.ledOn(x, 21*brightness, 21*brightness, 0*brightness); //gold
                    }
                }
                break;
            case 5:
                for(int x = 1; x < 12; x++){
                    if(x != mode){
                        b.ledOn(x, 0*brightness, 21*brightness, 21*brightness); //teal
                    }
                }
                break;
            case 6:
                for(int x = 1; x < 12; x++){
                    if(x != mode){
                        b.ledOn(x, 21*brightness, 0*brightness, 21*brightness); //pink
                    }
                }
                break;
            case 7:
                for(int x = 1; x < 12; x++){
                    if(x != mode){
                        b.ledOn(x, 51*brightness, 21*brightness, 5*brightness); //pale orange
                    }
                }
                break;
            case 8:
                for(int x = 1; x < 12; x++){
                    if(x != mode){
                        b.ledOn(x, 21*brightness, 51*brightness, 5*brightness); //light green
                    }
                }
                break;
            case 9:
                for(int x = 1; x < 12; x++){
                    if(x != mode){
                        b.ledOn(x, 5*brightness, 51*brightness, 21*brightness); //aqua teal
                    }
                }
                break;
            case 10:
                for(int x = 1; x < 12; x++){
                    if(x != mode){
                        b.ledOn(x, 5*brightness, 15*brightness, 2*brightness); //light green again
                    }
                }
                break;
            case 11:
                for(int x = 1; x < 12; x++){
                    if(x != mode){
                        b.ledOn(x, 11*brightness, 2*brightness, 36*brightness);//purple
                    }
                }
                break;
            
        }//closes switch
        //increse the brightness modifier
        if (brightness < 4){brightness++;}
        else{brightness = 1;}
        //update the delay timer
        fadeDelay = millis(); 
        }//closes delay if
    }//closes animate method

I’ll flash that to my Internet Button some time this week and see how to get you unstuck :wink:

@dadiletta, the most obvious error here is that you don’t seem to subscribe to any of your responses.

You need to publish an event that has a name starting with your prefix in order to trigger the handler.
I thought you’d also like who responded how, so the event-name building would just stay the same for each respinse type, only the msg carries the response type.

I’m not sure if you actually want to act on press-and-hold a button, but looking at the rest of your code I’d not think so (it wouldn’t work well that way ;-))
So I’d do this

        if(b.buttonOn(1) && millis() - modeDelay > 250) {
          modeDelay = millis(); 
          mode++;
          if(mode > 11) mode = 1;

          while(b.buttonOn(1)) Particle.process(); // wait till button gets released
          showMode = true;
          b.allLedsOff();
        }

The next thing is that you will never set needPublish to true again - that’s how static variables work.
They are only initialized once and then keep the value they last were assigned.

    void publisher(const char *msg) {
        static uint32_t ms;       // to keep track of last publishing time
        char evt[32];
  
        snprintf(evt, sizeof(evt), "%s%d", evtPrefix, playerID);
        {
            while(millis() - ms < 1010) Particle.process();  // wait till we are allowed to publish
            ms = millis();      // the last publish is long enough gone
        } while(!Particle.publish(evt, msg));  // repeat while not successful
       
        while(b.buttonOn(1) || b.buttonOn(2) || b.buttonOn(3) || b.buttonOn(4)) 
          Particle.process();  // stay locked here till all buttons are released
     }//closes publisher

This fixes the blocking but causes two publishes.

There is also some potential for streamlining this code especially in the parts where you do mostly the same things in all these switch case blocks.
I’m down to 144 lines of code :wink:

Whos Particle account will all your Photons be claimed to?
If they will all belong to the same account, you should consider Particle.publish(evt, msg, PRIVATE) and Particle.subscribe(evtPrefix, inviteHandler, MY_DEVICES), since currently everybody who has this code will take part on your party :sunglasses:

1 Like

Whooooo! It’s working! This is so cool, thanks. All I’ve done so far is remove the “static” in front of needPublish and it works beautifully. My brothers commented on my earlier version that they wanted to hold down the mode selector to quickly cycle through the modes, which is why I set it up like that.

These buttons will be spread across 6 different Particle accounts by the time I’m done (big family). So I’ll just change the evtPrefix so we’ll listen for a different trigger than what’s posted publicly here.

Next up, I’ll be trying to figure out a 10 minute timer that automatically moves off of mode 0 (listening mode) so none of my brothers see their Button glowing with green dots and get excited to play a game of Overwatch that actually ended hours ago. I should be able to figure that out using a similar structure as our millis().

I teach an intro to programming class at my high school, so I’m very interested in how you streamlined the switch blocks!

Again, thanks for all your help. This has been a fun challenge.

1 Like

Using arrays helps doing such stuff in loops rather than laborious switch-cases.
Also in your animation if you move the for() out and around the switch-case (since it’s the same for all) you can cut the code.

Here you are :wink:

#include "InternetButton/InternetButton.h"
InternetButton b;

//This goes in front of all players as the hook for our Particle.subscribe
const char evtPrefix[] = "InvteFrmPlayer_";

const char modeMsg[][16] =
{
    "Mode%dAction%d",
    "Wanna play?",
    "Sure!",
    "Soon",
    "Can't",
};    

const int modeColor[][3] = 
{
    { 0, 0, 0},     //  0 black (dummy)
    {21, 0, 0},     //  1 red
    { 0,21, 0},     //  2 green
    { 0, 0,21},     //  3 blue
    {21,21, 0},     //  4 yellow
    { 0,21,21},     //  5 cyan
    {21, 0,21},     //  6 magenta
    {51,21, 5},     //  7 pale orange
    {21,51, 5},     //  8 light green
    { 5,51,21},     //  9 aqua teal
    { 5,15, 2},     // 10 dull green
    {11, 2,36},     // 11 purple
};

//This changes based on who has the device
const int playerID = 6;

//this helps the mode and mode animations
int mode = 1;
bool modeReady = true;
bool showMode = true;
int brightness = 1;

void setup() 
{
    //We subscribe to evtPrefix so any event that starts with it will trigger 
    Particle.subscribe(evtPrefix, inviteHandler, MY_DEVICES);
    // to set all the pinModes and initialze any interface used 
    b.begin();
}

void loop() {
    //--------------------------------
    //------BUTTERY MODE SELECTOR-----
    //--------------------------------
    static uint32_t modeDelay;       // to keep track of last publishing time
    int btnNr;
    // get number of first pressed button
    for (btnNr = 1; btnNr <= 4 && !b.buttonOn(btnNr); btnNr++);
    btnNr %= 5; // reset to 0 if none wase pressed
  
    while(!b.allButtonsOff()) Particle.process(); // wait till all buttons get released again

    if(btnNr == 1 && millis() - modeDelay > 250) {
      modeDelay = millis(); 
      if(++mode > 11) mode = 1;
      showMode = true;
    }

    if(showMode){
        b.ledOn(mode, 85, 85, 85);
        animateMode();
    } 

    if (btnNr < 2 || 4 < btnNr) return; // btnNr out of bounds! 
    
    switch (mode)
    {
        case 0:
            publisher(modeMsg[btnNr]);
            break;
        case 1:
            publisher(modeMsg[1]);
            break;
        default:
            char msg[32];
            snprintf(msg, sizeof(msg), modeMsg[0], mode, btnNr-1);
            publisher(msg);
            break;
    }//closes switch
    delay(50);
}//close loop()

//--------------------------------
//------IFTTT and HANDLERS--------
//--------------------------------
void publisher(const char *msg) {
    static uint32_t ms;       // to keep track of last publishing time
    char evt[32];

    snprintf(evt, sizeof(evt), "%s%d", evtPrefix, playerID);
    while(millis() - ms < 1010) Particle.process();  // wait till we are allowed to publish
    ms = millis();      // the last publish is long enough gone
    Particle.publish(evt, msg, PRIVATE);  // repeat while not successful
    
    while(!b.allButtonsOff()) Particle.process();  // stay locked here till all buttons are released
}//closes publisher

void inviteHandler(const char *event, const char *data)
{
    if(showMode){
        mode = 0;
        b.allLedsOff();  
        showMode = false;
        //TODO: start a timer to switch off of listen mode
    }
    int player = atoi(&event[strlen(evtPrefix)]);
    if (!strcmp(data, modeMsg[1]) || !strcmp(data, modeMsg[2])) {
        b.ledOn(player, 0, 255, 0);
    }
    else if(!strcmp(data, modeMsg[3])) {
        b.ledOn(player, 0, 0, 255);
    }
    else if(!strcmp(data, modeMsg[4])) {
        b.ledOn(player, 255, 0, 0);
    }
}

//--------------------------------
//------ANIMATIONS----------------
//--------------------------------
void animateMode()
{
    static uint32_t fadeDelay;       // to keep track of last publishing time
    if(millis() - fadeDelay > 550)
    {
        for(int x = 1; x < 12; x++){
            if(x != mode)
                b.ledOn(x, modeColor[mode][0]*brightness, modeColor[mode][1]*brightness, modeColor[mode][2]*brightness);
        }
        //increse the brightness modifier
        brightness++;
        if (brightness > 4) brightness = 1;
        //update the delay timer
        fadeDelay = millis(); 
    }//closes delay if
}//closes animate method
1 Like