1 Code base, many Xenons


#1

I tried to search, so sorry for a likely ridiculous question. Wasn’t sure what category for this.

Setting up motion sensors in the woods, having a boron with many xenons. I want one code base for each, but I want each “node” to send info so I can know exactly which sensor goes of. That would allow me to track motion across the property.

But is there a more elegant way to do this than a 15 IF () statements? I started here and thought there had to be a better way:

    if (digitalRead(D1) == HIGH) {
        if (String(devname) == "Zone01-01") {
            String devLocID = "11";
        }
        else if (String(devname) == "Zone01-02") {
            String devLocID = "12";
        }

Thanks for any help. Point me to tutorials if there are some, I am happy to research/read/learn.

Rick


#2

Several questions before I could give you a really useful answer. There are many ways to optimize this code, but first think about your actual requirements.

Where do you set devname? Is it set in the cloud or in code? If it’s in code then you don’t really have a single codebase.

If devname is in the cloud, and you’re retrieving it, why not instead or retrieve both devname and devLocID?

If you’re just looking to differentiate between devices, then why do a string comparison and static response at all? Just send devname to the cloud.

I realize this may just be skeleton code, but it would be helpful to understand exactly what you’re trying to accomplish with the variables devname and devLocID, where they come from, and what uses them on the backend. Presumably you have some human interface to this to see which sensors tripped.

Lastly, and if I don’t say it @ScruffR will. :slight_smile: I highly recommend you do not use String objects, and instead use proper C strings (i.e. char foo[xx]). Strings in a limited RAM device like this cause heap fragmentation, which will over time cause malfunctions or a crash.


#3

@picsil Good questions - Thanks a bunch for helping. The first thing that jumped out was about use of strings… I create devname as char, based on @ScruffR here learning to get the device name. Was trying to set up webhooks to check batt voltage and as step 1 in logging motion locations. I change devname to string only because in my limited experience and habit, that’s what I had learned to do (I forget what example).

Devname and devLocID - retrieve and use the device name from the cloud to set devLocID. devLocID is in code so I know the event (Motion) and which sensor (devLocID) spread across a couple acres. I was hoping that I could use one codebase that could work on all sensors, and just determine which one the code is running on. Right now, it’s just testing 5 sensors in Zone 1, so numbered devLocID 11 - 15. Zone 2 will be 21 - 25, etc.

I’m using thingspeak.com to learn webhooks and see results for now. Eventually, I want to send a text with this info and more, as well as log the monitor voltage, when motion occurs and by which of 15-20 xenons are tripped.

Below is the full code. It has a lot of other garbage in it as it’s my first attempt at many things, had stuff added/moved as it grew. So it’s ugly, but it’s current state. Sleep/battery management, etc. are future needs. For now, I want to see how long a battery might last, and track the sensor tripped.

// Zone1 01 Motion

int led1 = D0;
int led2 = D7;
int motion = D1;
int n = 0;
int old_time = 0;
String MyID = System.deviceID();
String MotionName = "Starter";
char devname[32] = "";
bool publishName = false;

// DeviceName Handler
void devnameHandler(const char *devn, const char *data) {
    strncpy(devname,data,sizeof(devname)-1);
    publishName = true;
}

void setup() {
    pinMode(D0, OUTPUT);
    pinMode(D1, INPUT);
    // function from other project:
    Particle.function("led",ledToggle);
    digitalWrite(led2, LOW);
    Particle.function("chkVolt",checkvolt);
    // Get DeviceName for standardized code
    Particle.subscribe("particle/device/name", devnameHandler);
    Particle.publish("particle/device/name");
}

void loop() {
    if (millis() - old_time > 900000) {
        float voltage = analogRead(BATT) * 0.0011224;
        Particle.publish("voltage", String(voltage), PRIVATE);
        old_time = millis();
    }
    if (digitalRead(D1) == HIGH) {
        if (String(devname) == "Zone01-01") {
            String devLocID = "11";
        }
        else if (String(devname) == "Zone01-02") {
            String devLocID = "12";
        }
        else if (String(devname) == "Zone01-03") {
            String devLocID = "13";
        }
        else if (String(devname) == "Zone01-04") {
            String devLocID = "14";
        }
        else if (String(devname) == "Zone01-05") {
            String devLocID = "15";
        }
        Particle.publish("Motion", "11", PRIVATE);
        digitalWrite(led1,HIGH);
        while (digitalRead(D1) == HIGH); // hang tight here until motion stops
        digitalWrite(led1,LOW);
    }
    else {
        MotionName = 'Unknown';
    }
}

int checkvolt(String voltreport) {
    if (voltreport == "report") {
        float voltage = analogRead(BATT) * 0.0011224;
        Particle.publish("voltage", String(voltage), PRIVATE);
        
    }
}

Hope I actually answered the main questions.


#4

If you keep to the naming convention you already have there then this should help
(and do away with String as @picsil already mentioned)

char myID[25] = "";
char motionName[16] = "Starter"; // I'd rather use an enum for these tho'
char devName[32] = "";
int  myZoneID = 0;

void devnameHandler(const char *devn, const char *data) {
  int zone = 0;
  int dev  = 0;
  strncpy(devName, data, sizeof(devName) - 1);
  strncpy(myID, System.deviceID(), sizeof(myID) - 1);
  if (2 == sscanf(devName, "Zone%d-%d", &zone, &dev)) // when we got a valid devName
    myZoneID = zone*10 + dev;                         // take the two numeric parts and pull them together
  publishName = true;
}

No idea how long your motion will be but if it’s more than 10 sec this will probably break the connection

You should be calling Particle.process() in that loop.

This should be double quotes (") - single quotes (') are reserved for single characters in C.

BTW, to get rid of your String comparisons: C string comparisons are commonly done via strcmp()

And to wrap a numeric into a string snprintf() can be used
e.g

  char msg[16];
  snprintf(msg, sizeof(msg), "%d", myZoneID);
  Particle.publish("Motion", msg, PRIVATE);

See printf() to learn what formatting optinos you have with that.

However, why not just publish the device name (as @picsil already suggested) which has a more readable format that just an anonymous number which you need to know how to read in order to separate zone from device again?


#5

Here is a different strategy that I use - perhaps you can emulate?

I use Thingsboard.io (TB) as my data platform and form my (basic) research - seems to do similar things as Thingspeak.

In TB I register each device with its mac address ( and in the app on the device - i get that mac address as use that as the login to TB) - now I have a unique device in TB. TB has a “shared attributes” function that allows you to define a key pair on the TB server and it will be available to the associated device. So here I then name each device and from then on I can use the device id(mac) or its assigned name in any of the rules or display nodes in the TB system - if you have a display on the device then can be used as a local ad as well - although in your case your power requirements will preclude a display I guess.


#6

@ScruffR - Thanks… Have some homework on the language. C is brand new to me.

  if (2 == sscanf(devName, "Zone%d-%d", &zone, &dev)) // when we got a valid devName
    myZoneID = zone*10 + dev;                         // take the two numeric parts and pull them together

I don’t yet understand the use of sizeof() yet (why the -1?) in the handler, but the “if (2 == sscanf(…)” I’ll have to step through, solving for the variables, as it were.

Renaming the devices to “Zone11”, for “Zone01, sensor 01” might be easier, then just eliminate “Zone” from the string to get devLocID.

As for just passing the device name, that was what I was going to do, but when I started on the Thingspeak webhook, I assumed I needed numeric data to see a chart, so just changed it to integers based on the device name. Perhaps in the future text message I’ll do that.

Particle.process() - I just read about that yesterday, and thought about using it, but it did not hit me that the WHILE loop would need it.

Time for the real job, and homework on the side. I’ll test your suggestions to help me learn what it’s doing.

Thank you very much.

@shanevanj - haven’t had a chance to digest yours, but will do so this evening. Thanks for chiming in!


#7

You are reserving some portion of RAM to hold your string and sizeof() gives you the amount you have reserved, but a C string always takes one extra byte to “terminate” the string (zero-terminator \0), hence you only can store a string that takes one byte less then what you haver reserved.

sscanf() returns the number of how many variables could be filled from the provided string. We are expecting two variables so when the return value equals our expectation (2) we can proceed with and use the found values.

True, but the above isn’t unmanagably complicated either :wink: