Need delay() or lots of code? Try a SAM/SAMD board


#1

Thought I would share this tidbit of info. Using the newer features of the Arduino SAM(Due) or SAMD(Zero, MKRZero, MKR1000, Sparkfun dev board) you can actually dedicate a loop for cayenne.run() to be called. You can run your other code and use delay(), run extensive “for” or “while” loops, or just to make sure cayenne is called enough so it stays connected. I’m working on making a new crawler robot and this is a life saver.

I know I have seen some people needing to use delay functions but can’t because it causes cayenne to disconnect. Hope this helps. I included my code as example showing this. Just add the library “Scheduler” by Arduino, then call your additional loops in Setup(), and then place your code in the respective loop, loop2, or loop3. The Arduino uses the 32bit processor to simultaneously run all of them.

Code:

#include <Arduino.h>
#include <CayenneESP8266Shield.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include <Scheduler.h>


char token[] = "";
char ssid[] = "";
char password[] = "";

#define EspSerial Serial1
ESP8266 wifi(EspSerial);

Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_DCMotor *moto1 = AFMS.getMotor(1);
Adafruit_DCMotor *moto2 = AFMS.getMotor(2);


#define onBoardLED 13

int i;

byte disco = 0;
byte directionBut;

bool drive1 = false;
bool drive2 = false;
bool online = false;

unsigned long time1;
unsigned long onlineTime;
unsigned long prevTime;




void setup()
{
	SerialUSB.begin(9600);
	delay(10);

	EspSerial.begin(9600);
	delay(10);

	AFMS.begin();

	Cayenne.begin(token, wifi, ssid, password);

	pinMode(onBoardLED, OUTPUT);

	Scheduler.startLoop(loop2);
	Scheduler.startLoop(loop3);
}





void loop()
{
	Cayenne.run();
}




void loop2()
{
	time1 = millis();

	if (drive1 == true)
		{
			if (directionBut == 0)
				{
					moto1->run(FORWARD);
					moto1->setSpeed(255);
					moto2->run(FORWARD);
					moto2->setSpeed(255);
				}

			else
				{
					moto1->run(BACKWARD);
					moto1->setSpeed(255);
					moto2->run(BACKWARD);
					moto2->setSpeed(255);
				}
		}

	else
		{
			moto1->run(RELEASE);
			moto2->run(RELEASE);
		}

	yield();
}



void loop3()
{
	if (online)
		{
			for (i = 250; i > 30; i--)
				{
					analogWrite(onBoardLED, i);
					delay(2);
				}

			for (i = 30; i < 250; i++)
				{
					analogWrite(onBoardLED, i);
					delay(2);
				}
		}
	delay(250);

	yield();
}







CAYENNE_CONNECTED()
{
	online = true;
	for (i = 0; i < 255; i++)
		{
			analogWrite(onBoardLED, i);
			delay(5);
		}
}


CAYENNE_DISCONNECTED()
{
	online = false;

	for (i = i; i > 0; i--)
		{
			analogWrite(onBoardLED, i);
			delay(5);
		}

	prevTime = millis();
	disco++;
}



CAYENNE_OUT(V23)
{
	Cayenne.virtualWrite(V23, disco);
}

CAYENNE_OUT(V24)
{
	float time2 = (time1 / 1000) / 60;
	Cayenne.virtualWrite(V24, time2);
}

CAYENNE_OUT(V25)
{
	onlineTime = ((time1 - prevTime) / 1000) / 60;
	Cayenne.virtualWrite(V25, onlineTime);
}


//////////////////////////////////////////////////////////////////////


CAYENNE_IN(V10)
{
	drive1 = getValue.asInt();
}

CAYENNE_IN(V11)
{
	directionBut = getValue.asInt();
}

#2

Very cool, thanks for posting this @vapor83!

Curious if you have found sweet spot for calling Cayenne while still trying to conserve battery power. Would that be a main use case for this?

@adam @rsiegel @kreggly @ognqn.chikov notifying you guys of this :slight_smile:

-Benny


#3

P.S. Decided to move this topic to ‘The Library’, hope you don’t mind!


#4

This is very cool and going to be useful as a lot of people I’ve encountered are using delay().

Here is another option for replacing delay and still getting your Arduino code to behave with Cayenne if you don’t have one of these boards: using SimpleTimer


#5

Simpletimer is a good library and that is what I use on all my other boards. Very lightweight and easy to use.

https://playground.arduino.cc/Code/SimpleTimer


#6

I haven’t had much luck with trying to keep cayenne connected while being in any state of sleep on the SAMD boards. Haven’t tried the other boards yet. What I have done to get around this is (for now until I can figure out something better) is have a trigger button that enables the “sleep” on the SAMD boards but right before this it sends a serial command to the ESP8266 to deep sleep as well. Then to wake up everything, I have a low level interrupt that wakes the SAMD board and that interrupt function resets the ESP8266 from sleep.

This is where I can’t get cayenne to “wake” back up. For some reason it won’t try reconnecting. I’ve been digging thru the libraries looking for a command to run to restart the connection process but no luck yet.

So, my work around is to enable the built in WDT (watch dog timer) for 1 second. That resets the board and thus cayenne is back online in about 5-10 seconds. I feel this is a real sloppy way to do this but until I find something better it works good. lol

Anyone have any ideas to restart cayenne without doing a reset? I’m going to play around with a uno tonight as they have more power saving options over the SAMD board. Maybe I can find a combo to keep enough of the board powered to run cayenne & stay connected and use a cayenne button to wake up. We’ll see.

PS, normal cayenne connected on the SAMD board with a ESP8266 for connection, draws 95-105mA idle, 140-160mA transmit. After sleep, it only pulls about 55-65mA.

void pwrDwn()
{
	// Serial.println("sleeping");
	EspSerial.println("AT+GSLP");  //put esp to sleep

	LowPower.sleep();  //put SAMD to deepest sleep available.

	// LowPower.standby();
	// LowPower.idle(IDLE_0);
}


void wakeUp()  //interrupt function called when set pin goes LOW
{
	delayMicroseconds(16383);  //allow time for board to wake completely up.  That duration is the max that will work in a interrupt routine accurately.
	// Serial.println("wakeup");
	afterWakeUp();  //mainly did this to get out of the interrupt function as things work weird in it.
}


void afterWakeUp()
{
	// Serial.println("afterWakeUp");
	EspSerial.println("AT+RST");  //wake/reset the ESP so we can connect.
	Watchdog.enable(1000);  //invoke the WDT to reset the SAMD board and connect to cayenne again.
}


CAYENNE_IN(V12)
{
	sleepEnable = getValue.asInt();
	if (sleepEnable == 1)
		{
			pwrDwn();  //start sleep sequence when button is pushed on dashboard
		}
}

#7

I made some progress with running cayenne in a power saving mode. So using the MKR1000 board and the Wifi101 library I was able run Cayenne without issues & dropped my power consumption quite a bit. It was easy to do once I found the info. This should work on the MKR1000 or any board using the Arduino WiFi Shield 101.

Cayenne connected, normal power mode ----------- 105 - 140mA on MKR1000
Cayenne connected, “low” power mode ----------------- 18 - 40mA on MKR1000

On a Cayenne_IN function, just call:

CAYENNE_IN(V22)
{
	int sleepButton = getValue.asInt();
	if (sleepButton == 1)
		{
			WiFi.lowPowerMode();
		}
	else
		{
			WiFi.noLowPowerMode();
		}
}

#8

In case anyone runs across this, we’ve bound that our Arduino MQTT Library doesn’t compile for the SAMD boards right now. We have some business partners who need it though, so this should be fixed relatively soon :slight_smile:

Just figured I’d post here and save anyone some grief/research.

Our regular Arduino libraries (what’s been posted about above) should compile OK with these boards, however.