arduino/plant_reference.ino (212 lines of code) (raw):
#include "plant_reference.h"
void setup()
{
Serial.begin(115200);
expresslink_serial.begin(115200);
Wire.begin(); // Join I2C Bus
LowPower.begin();
tempSensor.begin();
soilSensor.begin(0x36); //0x36 is the soil sensors I2C address
pinMode(powerPin, OUTPUT); // configure D7 pin as an OUTPUT
digitalWrite(powerPin, LOW); // turn the sensors OFF
pinMode(waterPin, OUTPUT); // configure D6 pin as an OUTPUT
digitalWrite(waterPin, LOW); // turn the water pump OFF
pinMode(LED_BUILTIN, OUTPUT);
do
{
// UNCOMMENT THE LINE BELOW IF RECONFIGURING EXPRESSLINK
// expresslinkInit();
expresslinkExecuteCommand("AT+RESET\n");
delay(timeout_ms);
expresslinkExecuteCommand("AT+CONF Topic1=PlantData\n");
expresslinkExecuteCommand("AT+CONF EnableShadow=1\n");
expresslinkExecuteCommand("AT+CONF Shadow1=PlantShadow\n");
expresslinkExecuteCommand("AT+CONNECT\n");
} while (!startsWith(expresslinkResponse, "OK", 2));
sendShadowCommandSeq("AT+SHADOW1 INIT\n", "AT+Event?\n", "OK 20", "OK 21");
sendShadowCommandSeq("AT+SHADOW1 UPDATE {\"state\":{\"desired\":{\"water\": \"off\" } } }\n",
"AT+SHADOW1 GET UPDATE\n",
"OK 1",
"OK 0");
sendShadowCommandSeq("AT+SHADOW1 SUBSCRIBE\n", "AT+Event?\n", "OK 26", "OK 27");
}
void loop()
{
if (!otaInProgress) {
do {
expresslinkExecuteCommand("AT+Event?\n");
if (startsWith(expresslinkResponse, "OK 3", 4)) {
expresslinkExecuteCommand("AT+CONNECT\n");
while (!startsWith(expresslinkResponse, "OK", 2)) {
Serial.printf("Attempting to reconnect\n");
expresslinkExecuteCommand("AT+CONNECT\n");
}
} else if (startsWith(expresslinkResponse, "OK 24", 5)) {
updateShadow();
} else if (startsWith(expresslinkResponse, "OK 5", 4)) {
ota();
}
} while (startsWith(expresslinkResponse, "OK ", 3));
// get and send data every 5 min
if (millis()-lastDataSend >= sendInterval) {
lastDataSend = millis();
getData();
sendData();
}
}
if (otaInProgress || awaitingOTAResponse) {
ota();
}
if (waterOn) {
if (millis()-pumpStart >= pumpDuration) {
sendShadowCommandSeq("AT+SHADOW1 UPDATE {\"state\":{\"desired\":{\"water\": \"off\" } } }\n",
"AT+SHADOW1 GET UPDATE\n",
"OK 1",
"OK 0");
} else if(!otaInProgress && !awaitingOTAResponse) {
delay(pumpDuration);
}
} else if (!otaInProgress && !awaitingOTAResponse) {
delay(sendInterval-(millis()-lastDataSend)); //adjust sleep to ensure data sent every sendInterval
}
}
void getData()
{
digitalWrite(powerPin, HIGH); // turn water sensor and photocell on
// read temperature data
tempSensor.wakeup();
temperature = tempSensor.readTempF();
tempSensor.sleep();
soilMoisture = soilSensor.touchRead(0);
// read water level in tank
waterLevel = analogRead(waterSensorPin);
// read light data
light = analogRead(lightSensorPin);
digitalWrite(powerPin, LOW); // turn water sensor and photocell off
}
void sendData()
{
digitalWrite(LED_BUILTIN, HIGH); // Blink stat LED
sprintf(MQTTMessage,
"AT+SEND1 { \"temperature\": %i, \"waterLevel\": %i, \"light\": %i, \"soilMoisture\":%i }\n",
temperature,
waterLevel,
light,
soilMoisture);
// send the json payload over MQTT
expresslinkExecuteCommand(MQTTMessage);
digitalWrite(LED_BUILTIN, LOW); // Turn off stat LED
}
void expresslinkInit()
{
if (MY_AWS_IOT_ENDPOINT == "" || MY_SSID == "" || MY_PASSPHRASE == "")
{
Serial.printf("Need to define IoT Endpoint and WIFI\n");
exit(1);
}
expresslinkExecuteCommand("AT+CONF? ThingName\n");
expresslinkExecuteCommand("AT+CONF? Certificate\n");
expresslinkExecuteCommand("AT+CONF Endpoint=" MY_AWS_IOT_ENDPOINT "\n");
expresslinkExecuteCommand("AT+CONF SSID=" MY_SSID "\n");
expresslinkExecuteCommand("AT+CONF Passphrase=" MY_PASSPHRASE "\n");
}
void expresslinkSetParams() {
expresslinkExecuteCommand("AT+CONF? Version\n");
expresslinkExecuteCommand("AT+CONF Topic1=PlantData\n");
expresslinkExecuteCommand("AT+CONF EnableShadow=1\n");
expresslinkExecuteCommand("AT+CONF Shadow1=PlantShadow\n");
sendShadowCommandSeq("AT+SHADOW1 INIT\n", "AT+Event?\n", "OK 20", "OK 21");
sendShadowCommandSeq("AT+SHADOW1 SUBSCRIBE\n", "AT+Event?\n", "OK 26", "OK 27");
}
void expresslinkExecuteCommand(char *command)
{
Serial.printf(command);
expresslink_serial.printf(command);
expresslink_serial.setTimeout(timeout_ms);
String response = expresslink_serial.readStringUntil('\n');
int responseLen = response.length() + 1;
response.toCharArray(expresslinkResponse, responseLen);
Serial.printf(expresslinkResponse);
}
void updateShadow(){
expresslinkExecuteCommand("AT+SHADOW1 GET DELTA\n");
JsonObject& delta = jsonBuffer.parseObject(String(expresslinkResponse).substring(3));
const char* updateMes = delta["state"]["water"];
waterOn = strcmp(updateMes, "on") == 0;
if (waterOn) {
pumpStart = millis();
Serial.printf("WATER PUMP ON\n");
digitalWrite(waterPin, HIGH);
} else {
Serial.printf("WATER PUMP OFF\n");
digitalWrite(waterPin, LOW);
}
}
bool startsWith(char* response, char *str, int len) {
char subbuff[len+1];
memcpy(subbuff, &response[0], len);
subbuff[len] = '\0';
return strcmp(subbuff, str) == 0;
}
void sendShadowCommandSeq(char* command, char* checkRes, char* success, char* fail) {
do {
if (startsWith(expresslinkResponse, "ERR8", 4)) {
expresslinkExecuteCommand("AT+CONF EnableShadow=1\n");
expresslinkExecuteCommand("AT+CONF Shadow1=PlantShadow\n");
}
expresslinkExecuteCommand(command);
retries = 0;
do {
delay(2000);
expresslinkExecuteCommand(checkRes);
retries++;
} while (retries<5
&& !startsWith(expresslinkResponse, fail, strlen(fail))
&& !startsWith(expresslinkResponse, success, strlen(success)));
} while (!startsWith(expresslinkResponse, success, strlen(success)));
}
void ota() {
switch (state) {
case NO_OTA: {
expresslinkExecuteCommand("AT+OTA?\n");
if (startsWith(expresslinkResponse, "OK 0", 4)) {
awaitingOTAResponse = false;
otaInProgress = false;
} else if (startsWith(expresslinkResponse, "OK 1", 4)) {
awaitingOTAResponse = true;
state = DOWNLOAD_OTA;
}
break;
}
case DOWNLOADING: {
delay(timeout_ms);
expresslinkExecuteCommand("AT+OTA?\n");
if (startsWith(expresslinkResponse, "OK 4", 4)) {
state = OTA_UPDATE;
}
break;
}
case DOWNLOAD_OTA: {
Serial.printf("Would you like to download and flash the new ExpressLink firmware (y/n)?\n");
Serial.setTimeout(10000);
String userResponse = Serial.readStringUntil('\n');
if (userResponse.equals("y\r")) {
expresslinkExecuteCommand("AT+OTA ACCEPT\n");
state = DOWNLOADING;
otaInProgress = true;
awaitingOTAResponse = false;
} else if (userResponse.equals("n\r")) {
expresslinkExecuteCommand("AT+OTA FLUSH\n");
awaitingOTAResponse = false;
state = NO_OTA;
}
break;
}
case OTA_UPDATE: {
expresslinkExecuteCommand("AT+OTA APPLY\n");
state = NO_OTA;
otaInProgress = false;
expresslink_serial.setTimeout(20000);
String response = expresslink_serial.readStringUntil('\n');
int responseLen = response.length() + 1;
response.toCharArray(expresslinkResponse, responseLen);
Serial.printf(expresslinkResponse);
expresslinkExecuteCommand("AT+CONNECT\n");
expresslinkSetParams();
break;
}
}
}