// LAB1_6_cmdparm.ino
// dew.ninja August 2023
// adapted to Wokwi.com simulation on ESP32
// BH1750FVI is not available. So used value read
// from A/D pin instead.
// - read humidity, temperature, light data
// and publish to NETPIE 2020 together with a
// string that indicates place
// - implement commands to turn LEDs off/on
// using command=parameter structure
// #include <ESP8266WiFi.h>
#include <WiFi.h> // changed
#include <PubSubClient.h>
#include "DHT.h"
#include <Wire.h>
// #include <BH1750FVI.h> not available
//#include <analogWrite.h> // to use analogWrite on ESP32. Does not work!
#define LAMP 5 // changed from D5 to 5. Do this for all pins
#define VALVE 21
#define FERTILIZER 19
#define DHTPIN 4
#define DHTTYPE DHT22 // changed from DHT11
const char* ssid = "Wokwi-GUEST"; // this is always used for simulation
const char* password = ""; // no password
const char* mqtt_server = "broker.netpie.io";
const int mqtt_port = 1883;
const char* mqtt_Client = "";
const char* mqtt_username = "";
const char* mqtt_password = "";
const int lightPin = 34;
WiFiClient espClient;
PubSubClient client(espClient);
DHT dht(DHTPIN, DHTTYPE);
// remove because BH1750 is not available
//uint8_t ADDRESSPIN = 15; // D8, not used
//BH1750FVI::eDeviceAddress_t DEVICEADDRESS = BH1750FVI::k_DevAddress_L;
//BH1750FVI::eDeviceMode_t DEVICEMODE = BH1750FVI::k_DevModeContHighRes;
// Create the Lightsensor instance
//BH1750FVI LightSensor(ADDRESSPIN, DEVICEADDRESS, DEVICEMODE);
char msg[100];
unsigned long lastDHTRead = 0, newDHTRead=0;
// -------- global variables for command interpreter ----------
String rcvdstring; // string received from serial
String cmdstring; // command part of received string
String parmstring; // parameter part of received string
float parmvalfloat; //parameter variable (float)
int parmvalint; // parameter variable (int)
bool newcmd = 0; // flag when new command received
int sepIndex; // index of seperator
bool noparm = 0; // flag if no parameter is passed
int lampvalue=0; // lamp intensity
int valvevalue = 0; // valve
String fertilizer_state = "off";
int lamp=0, valve=0; // lamp and valve value between 0 - 100 %
String datastr; // data string used in command section
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection…");
if (client.connect(mqtt_Client, mqtt_username, mqtt_password)) {
Serial.println("connected");
// subscribe to command topics
client.subscribe("@msg/#");
}
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println("try again in 5 seconds");
delay(5000);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
String message;
for (int i = 0; i < length; i++) {
message = message + (char)payload[i];
}
Serial.println(message);
if(String(topic) == "@msg/cmd") {
rcvdstring=message;
cmdInt(); // call command interpreter
}
}
void setup() {
pinMode(LAMP, OUTPUT); // Grow lamp intensity 0 - 100%
pinMode(VALVE, OUTPUT); // water valve open 0 - 100%
pinMode(FERTILIZER, OUTPUT); // turn off/on fertilizer unit
analogWriteResolution(10);
Serial.begin(115200);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
dht.begin();
// LightSensor.begin();
}
void loop() {
newDHTRead = millis();
if (newDHTRead<lastDHTRead) lastDHTRead = newDHTRead; // prevent overflow
if(newDHTRead - lastDHTRead > 2000){
lastDHTRead = newDHTRead;
float humidity = dht.readHumidity();
float temperature = dht.readTemperature();
uint16_t light = analogRead(lightPin);
//uint16_t light = LightSensor.GetLightIntensity();
String place = "Farm 1";
if (!client.connected()) {
reconnect();
}
client.loop();
String data = "{\"data\": {\"humidity\":" + String(humidity) + ", \"temperature\":" + String(temperature)
+ ", \"light\":" + String(light)+ ", \"place\": \"" + String(place) + "\"}}";
Serial.println(data);
data.toCharArray(msg, (data.length() + 1));
client.publish("@shadow/data/update", msg);
}
while (Serial.available() > 0) { // detect new input
rcvdstring = Serial.readString();
newcmd = 1;
}
if (newcmd) { // execute this part only when new command received
cmdInt(); // invoke the interpreter
newcmd = 0;
}
delay(100);
}
void cmdInt(void)
{
rcvdstring.trim(); // remove leading&trailing whitespace, if any
// find index of separator ('=')
sepIndex = rcvdstring.indexOf('=');
if (sepIndex==-1) { // no parameter in command
cmdstring = rcvdstring;
noparm = 1;
}
else {
// extract command and parameter
cmdstring = rcvdstring.substring(0, sepIndex);
cmdstring.trim();
parmstring = rcvdstring.substring(sepIndex+1);
parmstring.trim();
noparm = 0;
}
// check if received command string is a valid command
if (cmdstring.equalsIgnoreCase("lamp")) {
if (noparm==1) {
Serial.println("Lamp intensity = "+(String)map(lampvalue,0,1023,0,100) + " %");
}
else {
parmvalint = parmstring.toInt();
if (parmvalint > 100) parmvalint = 100; // limit to 100%
else if (parmvalint<0) parmvalint = 0;
lamp = parmvalint;
lampvalue = map(lamp,0,100,0,1023);
analogWrite(LAMP,lampvalue);
datastr = "{\"data\": {\"lamp\":" + String(lamp)+"}}";
datastr.toCharArray(msg, (datastr.length() + 1));
client.publish("@shadow/data/update", msg);
Serial.print("Lamp set to ");
Serial.print(lamp);
Serial.println(" %");
Serial.println(datastr);
}
}
else if (cmdstring.equalsIgnoreCase("valve")) {
if (noparm==1) {
Serial.println("Valve opening = "+(String)map(valvevalue,0,1023,0,100) + " %");
}
else {
parmvalint = parmstring.toInt();
if (parmvalint > 100) parmvalint = 100; // limit to 100%
else if (parmvalint<0) parmvalint = 0;
valve = parmvalint;
valvevalue = map(valve,0,100,0,1023);
analogWrite(VALVE,valvevalue);
datastr = "{\"data\": {\"valve\":" + String(valve)+"}}";
datastr.toCharArray(msg, (datastr.length() + 1));
client.publish("@shadow/data/update", msg);
Serial.print("Valve set to ");
Serial.print(valve);
Serial.println(" %");
Serial.println(datastr);
}
}
else if (cmdstring.equalsIgnoreCase("fertilizer")) { // fertilizer feed
if (noparm==1) {
Serial.print("Current fertilizer state = ");
Serial.println(fertilizer_state);
}
else {
if (parmstring.equalsIgnoreCase("on")){
digitalWrite(FERTILIZER,1);
client.publish("@shadow/data/update", "{\"data\" : {\"fertilizer\" : \"on\"}}");
Serial.println("FERTILIZER ON");
fertilizer_state = parmstring;
}
else if (parmstring.equalsIgnoreCase("off")) {
digitalWrite(FERTILIZER,0);
client.publish("@shadow/data/update", "{\"data\" : {\"fertilizer\" : \"off\"}}");
Serial.println("FERTILIZER OFF");
fertilizer_state = parmstring;
}
else Serial.println("Invalid fertilizer state");
}
}
else { // no match
Serial.println("Invalid command.");
}
}