Semi-Automatic Mode Question

Hello everyone!

I am using the Spark Core to control a critical system. Basically I listen to 6 inputs, then based on these inputs I fire a few relays using some other IO’s. Because this operation is critical, I decided to use SYSTEM_MODE(SEMI_AUTOMATIC).

Now, when I power my Spark Core, my usercode runs where in the Setup() routine I set my default values/etc. Then I do a Spark.connect() and define my Spark.function(…). Now my usercode halts till the cloud is connected and then my Loop() runs.

My question is, what happens if the cloud disconnects or the wifi disconnects? Does the usercode still run, or will it halt till the cloud reconnects?

Hi @Carsten4207 thanks for the excellent question.

If the Cloud is disconnected intentionally in code, the Core will drop back to just a WiFi connection and the user code will still run.

If the WiFi is disconnected intentionally in code, the Core will drop back to just running user code.

If the Cloud disconnects unintentionally, the Core will continue to try to reconnect to the Cloud and will block execution of user code when attempting to connect to the Cloud.

If the WiFi disconnects unintentionally, the Core will continue to run but also try to reconnect to WiFi and will briefly block user code at the beginning of negotiating a WiFi connection (only when WiFi is present), but will run user code thereafter while trying to connect.

Here’s a nice example app that will help you test this behavior:

#include "application.h"

SYSTEM_MODE(SEMI_AUTOMATIC);

void tcp_connect();

TCPClient client;

void setup()
{
  // Make sure your Serial Terminal app is closed before powering your Core
  Serial.begin(9600);

  // Now open your Serial Terminal, and hit any key to continue!
  while(!Serial.available()) SPARK_WLAN_Loop();

  pinMode(D7,OUTPUT);
}

void loop()
{
  if (Serial.available()) {
    int c = Serial.read();
    switch (c) {
      case 'C': Spark.connect(); break;
      case 'c': Spark.disconnect(); break;
      case 'W': WiFi.connect(); break;
      case 'w': WiFi.disconnect(); break;
      case 'T': tcp_connect(); break;
      case 't': client.stop(); break;
      case 'O': WiFi.on(); break;
      case 'o': WiFi.off(); break;
      case 'L': WiFi.listen(); break;
    }
  }

  if (client.available())
  {
    char c = client.read();
    Serial.print(c);
  }

  // Blink the D7 LED so we can see when user code is running
  digitalWrite(D7, !digitalRead(D7));
  delay(50);
}

void tcp_connect() {
  Serial.println("connecting...");

  if (client.connect("www.textfiles.com", 80))
  {
    Serial.println("connected");
    client.println("GET /100/ad.txt HTTP/1.0");
    client.println("Host: www.textfiles.com");
    client.println("Content-Length: 0");
    client.println();
  }
  else
  {
    Serial.println("connection failed");
  }
}

For the unintentional WiFi disconnects, I find it’s easiest to use a uFL Core and disconnect the antenna. To disable the Cloud, you have to do something like create a firewall setting in your router that prevents your core from seeing the internet.

Since you have a mission critical application, please do test this and see if it meets your expectations. If not please let us know! :smile:

1 Like

Thanks @BDub! I will test this weekend.

I am controlling an HVAC motor based on what zones call for heat/cool/fan. This is why I say it’s critical.

By default the motor will spin regardless so if the Spark Core drops it will be OK.

I’ll post more details about the project when its complete.

But for now, I’ll get back if I need anymore advice. Thanks!

Very cool! Or maybe I should say very Hot! Hawt? Yeah, dats Hawt!!

A good practice for making something safe is to add a watchdog like supervising microcontroller to your project. In this case if you trust all of the sensor data that the spark core makes decisions on, but just don’t trust that your code will always run an be able to shut off a heater relay or something to that effect… you could add a supervising micro watchdog that also decides to turn on/off the same relay. If both the core and watchdog say so turn it on, it will… this is an AND function. You could send your watchdog updates from the core via SPI, I2C or UART pretty easily with specific messages to turn on or turn off that relay. If you Core stops executing for a bit due to WiFi dropping out, your watchdog micro won’t get the commands (or or off) for a bit and you can default to a fail safe state (heater relay off typically - heater relay ON might actually be fail safe in some places really far north :slight_smile: )

When designing mission critical things, this level of added complexity is often necessary so don’t think of it as a burden… but more of a challenge to satisfy all of the needs of your system in the fastest, least expensive, most optimized, most reliable way.

Yeah not a bad idea but I already have custom PCB Boards made. What would you use for a watchdog? Another Spark without the cloud/WiFi?

I replaced my simple PSC motor with a ECM motor (Evergreen IM). This motor has 5 additional wires (24vac) which you normally tie into the thermosts wires so you can set different speeds for different calls. I wanted more control so I used Fairchild MID400 chips to determine what the thermostat is calling for and them firing some relays to basically switch the speeds.

Good thing about this motor is even if I dont hookup any of the 5 speed wires, the motor still spins at a low efficent mode. So this is already an OK failsafe.

I already have the motor installed for about 3 weeks. I love the quietness and should be more power efficient. Now with my Spark Core controller I should be able to spin the motor up dynamically based on how many zones are calling/etc. :smile:

Once finished I’ll do a project share for sure.

Wow that sounds really cool.

Is the call for heat (gas/electric) different than blower/motor fan speed? If so, a low speed might not be enough to keep your furnace from overheating if left on continuously in that state. I can’t say for sure how all of these things stack up so take my concerns with several grains of salt… pepper is good too.

For the watchdog, I’d use something really simple and cheap like a PIC10LF322, PIC12LF1571, ATTINY25, or possibly even MSP430 variant. Or just go to Digikey, lookup microcontrollers, and sort by price and see what’s at the top of the list (lowest price) that can talk on I2C/SPI/UART and have a couple extra I/O available. ATTINY25 can do SPI and I2C in hardware for a cheap price, but the PICs are even cheaper if you can work out a software UART on the LF322 or use the HW UART of the LF1571. MSP430G2230 has SPI and I2C built in as well, and cheap enough. They all can be coded in C with free tools for the most part, you might want to buy a more optimized compiler to squeeze all of the space out of these small parts.

Typically with PSC motors you have 3-4 phases. Normally 2 of these phases are connected to the control board (one for heat and the other for cool). See picture below:

In my case WHITE is neutral, BLUE/RED/BLACK are speeds. But the speed difference between them is minor and power consumption is still high.

With the ECM Motor, basically the hookups (BLUE/RED) wires are bridged because they just turn the motor ON. Then with the low voltage cables (24vac) that tap into the thermostats wires determine the speeds (from 600 rpm to 1200 rpm).

My HVAC is gas and it has a temp sensor that shuts off the gas if it overheats. Same applies with the AC. If the coils freeze, then the compressor is shut off… And again, if no wires on the 24vac are connected, the motor still spins at low RPM.

I did some more testing with a Spark Core with a uF connector. Basically my usercode stops running when the Core disconnects from the cloud (flashing BLUE). If the Wifi is disconnected (flashing GREEN), the usercode runs. Not the most ideal situation, but should be ok.

On a side note, not sure if this is possible, but it would be cool if the Spark Core could call a function before the usercode stops running. Say void ExecutionPaused() { … }. This would allow me to set a default speed if something went wrong. Just a thought :smile:

Right now I intend to set the default speed to 1, then if the Spark Core has problems the Motor still spins at a decent speed.

We actually have this on the list of things to do! System events https://github.com/spark/firmware/issues/279

When it's flashing cyan, that means it's connected to wifi, but not the cloud, and is actively trying to connect to the cloud. User code will block in that state, but I see a workaround. If you run in semi-automatic or manual mode, when the internet connection drops you might be able to get it to just stay in breathing green mode (connected to wifi) and then detect that the cloud has lost connection and do whatever you need to do before attempting to reconnect to the cloud and block user code.

1 Like

Excellent! I like your idea. I’ll test this tonight. But in theory if the cloud disconnects then I can set my motor speed to X until connectivity is back.

Thanks

1 Like

Very useful. Testing: To disconnect the Cloud I disconnect the WAN port on the router. To disconnect WiFi I turn off the router.

1 Like

Yes, that's exactly what I want to do elsewhere. But how? As I see it if you're Cloud connected and the Cloud vanishes then user code blocks.

I just wanted to confirm that your suggestion works!

I check spark.connected(), if it returns false then I set my motor speed to 1. This way if the Cloud is disconnected, the HVAC doesn’t run at the lowest speed (0).

Thanks for your help!

1 Like

What do you do? Do you just test Spark.connected() every iteration of the loop() function? Or do you test it in an interrupt routine?

If in the loop then presumably you need to check before you hit any code such as delay(1000) which would then have Spark.process() called for you. Maybe Spark.process() no longer blocks if the Cloud goes away? I wish this were more clear. And that someone in the know would say what happens, otherwise it’s just our/my speculation.

I check the Spark.connected() every loop(). If it returns false, then I set my backup mode.

I dont think having a delay in your code causes an issue. Your loop code will finish before the Spark Core checks its Cloud/Wifi state.

I could be wrong, but thats how I read the source code:

https://github.com/spark/firmware/blob/master/src/main.cpp

Line 202

What does or does not happen re user code blocking in the different modes remains unclear. But I have seen clearly documented that delay() does cause Cloud housekeeping routines to be called.

I am not going to read the underlying code, I don’t have the time, and I want to use the Spark Core as if it were a (well documented) black box, but from all I have read I had been led to understand that if the Cloud is connected and the Cloud then goes away then, no matter which of the three modes you are in, you should expect your user code to block. You are getting in early, before it blocks, and setting the conditions you want while it is blocked because in SEMI_AUTOMATIC mode blocking code is called outside of your control.

I have previously suggested that there ought to be a way of not blocking the user code - of allowing the WiFi to remain usable while the Cloud is out. It seems to me from what you write here that were you to use MANUAL rather than SEMI_AUTOMATIC that you could decline to call Spark.process() if unexpectedly Spark.connected() returned false. Then your user code would not block, and you could continue to run your motors at whatever speed you liked, varying this as needs be while you waited for the cloud to come back.

This is all ill-informed speculation, and seems to contradict advise given elsewhere, and so it is all pending clarification from the development team.

1 Like

I think the Spark Core is rather well documented here: http://docs.spark.io/firmware/

From your question about delay() causing the cloud to disconnect. If you delay for more than 20 seconds, then the cloud timesout and you see a cyan flashing. But it starts flashing cyan when your loop() finishes.

Run this code to test your delay issues:

SYSTEM_MODE(SEMI_AUTOMATIC);

void setup() {

    pinMode(7, OUTPUT);
    pinMode(6, OUTPUT);
    digitalWrite(7, HIGH);
    Spark.connect();        
}

void loop() {    
   
    digitalWrite(7, HIGH);
    delay(100);
  
    digitalWrite(7, LOW);
    delay(100);
    
    if (Spark.connected() == false) {
        digitalWrite(6, HIGH);
    }
    else
    {
        digitalWrite(6, LOW);
    }
}

With this code you need 1 extra LED. I use the built in LED on D7 and another LED on D6.

If you have a uF Spark Core you can test this easily. Run the Spark Core and you’ll see the blue D7 LED flash. Then disconnect your uF antenna. Before it loses connectivity, the D6 LED will turn on and stay on until you plug the antenna back in. This will happen EVERY time.

This basically allows you to execute something BEFORE the usercode blocks (Cloud disconnected). In my case, I set some IO’s HIGH.

Hope that clarifies it for you.

1 Like

I have a very similar “problem” with my code. In my setup routine I set a PINmode to HIGH so my relay off. My problem was if the cloud or the internet is down - the core resets and the PIN goes back to LOW as long their is no connection. This switched the PINmode back to LOW and so my relay is is also on and current flows…
I do not want to waste energy and I do not want to see all the time if my light in the garden is on/off… :slight_smile: so I decide to use the SEMI_AUTOMATIC mode. Here´s my code:

if (Spark.connected() == false) { 
        Spark.connect();
        delay(1000);
            if (Spark.connected == false) {
                RGB.color(255,0,0);
            } else {
                RGB.color(0,0,255);
            }
        delay(19000);

So every time when the spark is not connected I force it again and again to build the connection.
This is the only time I use delay() in my loop. My other code uses threads completely delay free. But without a connection I could not run my code (ok some parts of it, but it is nearly useless). The second if /else statement is to give me a visual feedback and set´s the Core MainLED to red if their is no connection and blue if their is a connection. I do not want if the connection is down that my Core tries all the time a reconnect - because if their is no connection this could take some time. I think 20 seconds is fair to deal with this problem.

After all it runs now 20 days without a problem.

cheers clyde

@clyde a couple comments. Perhaps this is just a typo, but your second .connected should be the same as the first. Also since you are waiting up to 20 seconds anyway, it might be a good idea to wait a bit longer before checking if you are in fact connected the first time. I’ve had this take anywhere from less than a second up to 8 seconds which different routers and internet. And perhaps only wait the remainder of 20 seconds if not connected, like this:

if (Spark.connected() == false) { 
    Spark.connect();
    delay(4000);
    if (Spark.connected() == false) {
        RGB.color(255,0,0);
        delay(16000);
    } else {
        RGB.color(0,0,255);
    }
}    

You might not realize it, but the delay()'s are helping to restore the connection… they called SPARK_WLAN_Loop(); from within them. So code like this won’t do the same thing:

if (Spark.connected() == false) { 
    Spark.connect();
    uint32_t startTime = millis();
    while ((millis() - startTime) < 20000UL) {
        if (!Spark.connected()) {
            RGB.color(255,0,0);
        } else {
            RGB.color(0,0,255);
            break;
        }
    }
}    

We currently have an issue logged to resolve this. Knowing what’s happening is half the battle though, so you can code more effectively. Basically all you need is something like this:

if (!Spark.connected()) { 
    RGB.color(255,0,0);
    Spark.connect();
    while (!Spark.connected()) {
        SPARK_WLAN_Loop();
    }
    RGB.color(0,0,255);
}    
3 Likes

@BDub:
Thank you for your input :slight_smile: very appreciated!
I know that the second loop does the exactly same as the first one, but I want to do things like

  • First if/else Is the core connected
  • No - connect it
    • Now open a second if/else to prevent the execution of the code if the result is false. Change LED to red to get a little bit of visual feedback
  • Else we should get true - change the led color to blue and exit the second loop
    anything else (which should mean connection result is true) run my threads

But your example looks very nice!
I will try that and write about my results. Thanks mate!

Very useful, but as you said, paraphrasing: The situation is in flux. E.g. your advice to call SPARK_WLAN_Loop() [which I snarkily :smile: point out remains undocumented] is I think now deprecated, and we’re supposed to call Spark.process(). Or are we?

I think also there are WiFi housekeeping issues which need to be attended to even if not connected to the Cloud, and so I think Spark.process() [or the other function] need to be called anyway. The reassurance I seek in that regard is that Spark.process() won’t block if not connected to the Cloud.

That would allow us to write apps which are agnostic to the connection status of the Spark Core to the Cloud. One could write user code which you knew would not block, would use the Cloud when it was available, and not use it when not. And same for the WiFi.

But I think the advice above might be summarised as “quickly test Spark.connected() before the user code blocks.” Or, as may well be the case, am I missing something?

1 Like