/**********************************************************************************
* Dosing Pump BruControl nodeMCU http bridge
* Version: 1
*
* BruControl nodeMCU http bridge framework Version: 0.2
*
* This framework provides a bridge between a nodeMCU and BruControl using BruControl's
* http GET/PUT data exchange protocol.
* Basic flow
* 1. Every checkInterval each BruControl global variable in the globalsToRead array is read
* and its value is placed in the corresonding index in the globalReadValues array.
* 2. Every calcInterval user defined code is ran to:
* process inputs
* interface with devices
* calculate return values
* 3. Every sendInterval each BruControl global variable in the globalsToWrite array's
* corresponding value in the globalValuesToWrite array is sent back to BruControl.
*
* General use of this framework.
* Place your user code within the @@@@@@@@@@@ sections
* There should be no need to modify the code outside of these sections.
*
************************************************************************************/
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
* Interfaces to Jebao DP-4 auto dosing pump.
*
* Polling BruControl for DosingPumpSend updates
* DosingPumpSend is a boolean. Transitioning from false to true triggers triggers
* the following:
* 1. Reads DosingPump value (integer 0-3 for what pump to turn on)
* 2. Reads DosingTime value (integer representing how many milliseconds to turn
* the pump on for.
* 3. Doses the pump for the time called for.
* 4. Writes to DosingPumpSend to indicate that the dosing is complete in case
* multiple doses will be performed back to back.
*
* Jebao DP-4 Uses SNx4HC595 8-Bit Shift Register to select which pump to activate (0-3)
* and 4 ULN2003A darlington transistor arrrays to drive the pumps.
*
* This uses the shiftout function to write the bits into the shift register.
* Only one pump can be on at a time.
*
* Calibration from dispense volume to pump time will be accounted for in BruControl.
* Create a script to time how long it takes to fill a container to a desire volume.
* Use a button to capture the time once the calibration volume is reached.
*
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
/**********************************************************************************
* BruControl Settings
*
* Modify the following code for the number of globals you want to read and update
* from/to BruControl, with they names of each global as it is named in BruControl.
*
* Set the server IP address and port as needed.
*
***********************************************************************************/
// The following are used to define what global variables in BruControl to monitor and update.
#define READSIZE 3 // Number of global variables to read
#define WRITESIZE 1 // Number of global variables to update
// Optional to define indices for variables to make it easier to use them
// Read Array Indices
#define DOSINGPUMPSENDINDEX 0
#define DOSINGPUMPINDEX 1
#define DOSINGTIMEINDEX 2
// Write Array Indices
#define DOSINGPUMPSENDINDEX 0
// Create arrays
String globalsToRead[READSIZE] = { "DosingPumpSend", "DosingPump", "DosingTime" }; // List of global variables to read as they are named in BruControl
String globalReadValues[READSIZE]; // Array to store read values in
String globalsToWrite[WRITESIZE] = { "DosingPumpSend" }; // List of global variables to update as they are named in BruControl
String globalValuesToWrite[WRITESIZE]; // Array to store write values in
// BruControl Server settings
char serverAddress[] = "xxx.xxx.xxx.xxx"; // BruControl Server IP Address
int port = 8000; // BruControl Server port
// Scheduling interval
unsigned long int delayInterval = 1000; // milliseconds between each time the main loop runs
/**********************************************************************************
* End BruControl Settings
***********************************************************************************/
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
* Place user defined initialization code below
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
//Pin connected to latch pin (ST_CP) of 74HC595
#define LATCHPIN 12
//Pin connected to clock pin (SH_CP) of 74HC595
#define CLOCKPIN 14
////Pin connected to Data in (DS) of 74HC595
#define DATAPIN 13
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
* End User defined/modifiable variables
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
String readString; //String loaded with results from HTTP get requests
char temp_string[300]; // temp string used for sprintf's and other crap
ESP8266WiFiMulti WiFiMulti;
void setup() {
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
* Place user defined setup code below
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
// moved user defined setup first so pumps don't turn on randomly when powering on.
//set pins to output because they are addressed in the main loop
pinMode(LATCHPIN, OUTPUT);
pinMode(DATAPIN, OUTPUT);
pinMode(CLOCKPIN, OUTPUT);
registerWrite(8, HIGH); // clear register so no pumps are on
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
* End user defined setup code
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
Serial.begin(115200);
Serial.println();
WiFi.mode(WIFI_STA);
WiFiMulti.addAP("SSID", "PASSWORD"); //Change this to your Wifi settings
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(WiFi.localIP().toString());
delay(5000);
}
/**********************************************************************************
* Main Loop to get inputs from BruControl, process them, calculate outputs,
* and send updates back to BruControl
*
* This allows you to use global variables in BruControl to interface with
* unsupported devices.
*
***********************************************************************************/
void loop() {
Serial.println("### Starting Main Loop ###################################################");
Serial.println(" ## Starting to read variables from BruControl #####");
getGlobals(); // get updates from BruControl
Serial.println(" ## Done reading variables from BruControl #####");
Serial.println(" @@ Starting to Parse and Caculate user code @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@U@@@@@@@@@@@@");
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
* Place user function calls here
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
if(globalReadValues[DOSINGPUMPSENDINDEX] == "True") {
registerWrite(globalReadValues[DOSINGPUMPINDEX].toInt(), HIGH); // turn on pump
delay(globalReadValues[DOSINGTIMEINDEX].toInt()); // wait correct amount of time
registerWrite(8, HIGH); // Turn off pump
globalValuesToWrite[DOSINGPUMPSENDINDEX] = "False"; // Tell BruControl we are done
}
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
* End user function calls
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
Serial.println(" @@ Done with user code @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
Serial.println(" ## Starting to write outputs back to BruControl #####");
sendGlobals(); // send updates to BruControl
Serial.println(" ## Done writing outputs to BruControl #####");
delay(delayInterval); // set delayInterval to how long you want to wait between executing the main loop
}
/**********************************************************************************
* End Main Loop
***********************************************************************************/
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
* Place user defined functions below for processing inputs, interfacing with
* devices, and calculating return values.
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
void registerWrite(int whichPin, int whichState) {
// the bits you want to send
byte bitsToSend = 0;
// turn off the output so the pins don't light up
// while you're shifting bits:
digitalWrite(LATCHPIN, LOW);
// turn off clock before writing since we clock on rising edge.
digitalWrite(CLOCKPIN, LOW);
// turn on the next highest bit in bitsToSend:
bitWrite(bitsToSend, whichPin, whichState);
// shift the bits out:
shiftOut(DATAPIN, CLOCKPIN, MSBFIRST, bitsToSend);
// turn on the output so the LEDs can light up:
digitalWrite(LATCHPIN, HIGH);
}
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
* End user defined functions
*
* Start of framework functions
* Don't modify the code below here!!!!
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
/**********************************************************************************
* getGlobals()
*
* Framework function to iterate through the array of BruControl global variables
* to get current values.
*
* This function will read all global variables in the globalsToRead array
* and fill the globalReadValues arrays with their current values.
*
* Do not modify this function. Instead set the following at the top of this file:
* READSIZE - number of global variables to update
* globalsToRead - names of global variables as they are named in BruControl
* globalReadValues - Values for each global variable
*
***********************************************************************************/
void getGlobals() {
int i;
for(i=0; i<READSIZE; i++) {
globalReadValues[i] = getVariableValue(globalsToRead[i]);
Serial.print(" readString Is: ");
Serial.print(globalsToRead[i]);
Serial.print(", ");
Serial.println(globalReadValues[i]);
}
}
/**********************************************************************************
* sendGlobals()
*
* Framework function to iterate through the array of BruControl global variables
* to send updates to.
*
* This function will send all global variables in the globalsToWrite array with
* corresponding values from the globalValuesToWrite array.
*
* Do not modify this function. Instead set the following at the top of this file:
* WRITESIZE - number of global variables to update
* globalsToWrite - names of global variables as they are named in BruControl
* globalValuestoWrite - Values for each global variable
*
***********************************************************************************/
void sendGlobals() {
int i;
for(i=0; i<WRITESIZE; i++) {
sendVariableValue(globalsToWrite[i], globalValuesToWrite[i]);
Serial.print(" Write: ");
Serial.print(globalsToWrite[i]);
Serial.print(", ");
Serial.println(globalValuesToWrite[i]);
}
}
/**********************************************************************************
* getVariableValue()
* globalVariable - name of global variable as it appear in BruControl.
*
* Framework function to get values of individual BruControl global variables.
* This function is called by the getGlobals() framework function.
* The user should not call this function directly
*
* returns value of global variable in String form
*
***********************************************************************************/
String getVariableValue(String globalVariable) {
String payload = "";
StaticJsonDocument<512> jsonDoc;
char temp1[100];
char temp2[100];
// wait for WiFi connection
if ((WiFiMulti.run() == WL_CONNECTED)) {
WiFiClient client;
HTTPClient http;
globalVariable.toCharArray(temp1,100);
sprintf(temp2, "/global/%s", temp1);
if (http.begin(client, serverAddress, port, temp2)) {
// start connection and send HTTP header
int httpCode = http.GET();
// httpCode will be negative on error
if (httpCode > 0) {
// HTTP header has been send and Server response header has been handled
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
payload = http.getString();
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
} else {
Serial.printf("[HTTP} Unable to connect\n");
}
}
payload.toCharArray(temp1,300);
DeserializationError error = deserializeJson(jsonDoc, temp1);
if(error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.c_str());
return "";
}
const char* returnValue = jsonDoc["Value"];
return returnValue;
}
/**********************************************************************************
* sendVariableValue()
* globalVariable - name of global variable as it appear in BruControl.
* variableValue - value of variable in string form to send back to BruControl.
*
* Framework function to send updates to individual BruControl global variables.
* This function is called by the sendGlobals() framework function.
* The user should not call this function directly
*
***********************************************************************************/
void sendVariableValue(String globalVariable, String variableValue) {
char temp1[100];
char temp2[100];
char temp3[100];
if ((WiFiMulti.run() == WL_CONNECTED)) {
WiFiClient client;
HTTPClient http;
globalVariable.toCharArray(temp1,100);
variableValue.toCharArray(temp2,100);
sprintf(temp3, "[{\"Name\":\"%s\",\"Value\":\"%s\"}]", temp1, temp2);
String jsonToWrite = temp3;
if (http.begin(client, serverAddress, port, "/globals")) { // HTTP
http.addHeader("Content-Type", "application/json");
http.addHeader("Content-Length", String(jsonToWrite.length()));
// start connection and send HTTP header
int httpCode = http.PUT(jsonToWrite);
// httpCode will be negative on error
if (httpCode > 0) {
// HTTP header has been send and Server response header has been handled
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
String payload = http.getString();
}
} else {
Serial.printf("[HTTP] PUT... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
} else {
Serial.printf("[HTTP} Unable to connect\n");
}
}
}