No need for photo.
This is the newest Could wifi hub and it doesn't work with the webwizard.
We will need to compile the code and upload it on your computer using the Arduino IDE.
It uses ESP8266 chip.
Start Arduino and open Preferences window.
into Additional Board Manager URLs field. You can add multiple URLs, separating them with commas.
Open Boards Manager from Tools > Board menu and find esp8266 platform.
Select the version you need from a drop-down box.
Click install button.
After it is installed, just to be sure, close Arduino and start it again to load everything it needs.
Then, go to menu Tools->Boards and you will see a bunch of ESP8266 boards. Pick the Adafruit HUZZAH ESP8266.
Code: Select all
#include <FS.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266httpUpdate.h>
#include <WiFiManager.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#include <Ticker.h>
#include <Wire.h>
#include <OneWire.h>
#include <CloudGlobals.h>
// ***********************************************************************
// Change fallback ports below
// Fallback ports will be turned on when cloud connection is lost
// Add each port separated by a |
// For example, if you would like for port 1 and port 5 of Relay Box 1 to be turned on when cloud connection is lost,
// you should add Port1Bit | Port5Bit
// It would look like this:
// #define Relay1FallBack Port1Bit | Port5Bit
#define Relay1FallBack 0
#define Relay2FallBack 0
#define Relay3FallBack 0
#define Relay4FallBack 0
#define Relay5FallBack 0
#define Relay6FallBack 0
#define Relay7FallBack 0
#define Relay8FallBack 0
// Redirect expansion modules to another custom expansion field.
// This is only required if you have multiple modules of the same parameter.
// For example, if you have one Cloud module with an existing salinity module already and wants to add another Cloud module
// with anoter salinity module and report those readings as a Custom Expansion Module 1
// In this case you have to change the CloudCustomModule1 to CUSTOM_SALINITY.
// The values that can be entered are:
// CUSTOM_NONE 0
// CUSTOM_SALINITY 1
// CUSTOM_ORP 2
// CUSTOM_PHEXP 3
// CUSTOM_WL 4
// CUSTOM_MULTI_WL1 5
// CUSTOM_MULTI_WL2 6
// CUSTOM_MULTI_WL3 7
// CUSTOM_MULTI_WL4 8
#define CloudCustomModule1 CUSTOM_NONE
#define CloudCustomModule2 CUSTOM_NONE
#define CloudCustomModule3 CUSTOM_NONE
#define CloudCustomModule4 CUSTOM_NONE
#define CloudCustomModule5 CUSTOM_NONE
#define CloudCustomModule6 CUSTOM_NONE
#define CloudCustomModule7 CUSTOM_NONE
#define CloudCustomModule8 CUSTOM_NONE
// Assign Temperature probes ID
// Only assign temperature probes IDs that you currently are not using in the head unit.
// If you have a total of more than 3 probes, make sure to add the following line on the setup section of the head unit code:
// ReefAngel.AddExtraTempProbes();
// For example, if you only have 1 probe connected to the head unit, assign IDs 2 and 3.
// If you have 2 probes in the head unit and 2 probes in the Cloud Wifi Hub, you must add the above line and you would assign
// IDs 3 and 4.
// Assign ID 0 to unused probes in the Hub.
byte TempProbeIDs[] = {4, 5};
// Do not change anything below
// ***********************************************************************
// solid green - factory reset
// blink green - connecting wifi
// blink blue - configuration required (SoftAP)
// blink magenta - connecting to cloud server
// solid cyan - connected to cloud server
// blinking cyan - transmitting/receiving data
// solid yellow - firmware update
#define LED_GREEN 2
#define LED_BLUE 0
#define LED_RED 16
#define RESET_ALL A0
#define INPUT1 12
#define INPUT2 13
#define TEMP_PIN 14
#define version "1.0.1"
#define MQTTServer "cloud.reefangel.com"
#define MQTTPORT 1883 // MQTT server port
WiFiManager wifiManager;
WiFiClient espClient;
PubSubClient CloudClient(espClient);
Ticker ticker;
OneWire ds(TEMP_PIN);
byte data[2];
byte addr[8];
byte addr1[8];
byte addr2[8];
boolean first_connection = true;
boolean shouldSaveConfig = false;
boolean updating = false;
unsigned long cloudmillis = millis();
unsigned long client_timeout = millis();
unsigned long serial_timeout = millis();
String currentLine = ""; // make a String to hold incoming data from the client
char mqtt_username[32];
char mqtt_password[32];
void APtick()
{
//toggle state
int state = digitalRead(LED_BLUE); // get the current state of GPIO1 pin
digitalWrite(LED_BLUE, !state); // set pin to the opposite state
}
void Wifitick()
{
//toggle state
int state = digitalRead(LED_GREEN); // get the current state of GPIO1 pin
digitalWrite(LED_GREEN, !state); // set pin to the opposite state
}
void Cloudtick()
{
//toggle state
int state = digitalRead(LED_BLUE); // get the current state of GPIO1 pin
digitalWrite(LED_RED, !state); // set pin to the opposite state
digitalWrite(LED_BLUE, !state); // set pin to the opposite state
}
void Datatick()
{
//toggle state
int state = digitalRead(LED_BLUE); // get the current state of GPIO1 pin
digitalWrite(LED_GREEN, !state); // set pin to the opposite state
digitalWrite(LED_BLUE, !state); // set pin to the opposite state
}
void LED_Color(int Red, int Green, int Blue)
{
analogWrite(LED_RED, 1023 - Red);
analogWrite(LED_GREEN, 1023 - Green);
analogWrite(LED_BLUE, 1023 - Blue);
}
int memory_read_int(int address)
{
return EEPROM.read(address);
}
void memory_write_int(int address, const int data)
{
if (memory_read_int(address) != data)
{
EEPROM.write(address, data);
EEPROM.commit();
}
}
unsigned int crc16(int *ptr, byte len)
{
unsigned int crc = 0xFFFF;
byte i;
byte temp = 0;
int test;
while (len--)
{
crc ^= *ptr++;
for (i = 0; i < 8; i++)
{
if (crc & 0x01)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
return crc;
}
void blink_data_led(byte timer)
{
digitalWrite(LED_RED, LOW);
delay(timer);
digitalWrite(LED_RED, HIGH);
}
void Publish(char* pub_buffer, char* buffer)
{
CloudClient.publish(pub_buffer, buffer);
Serial.print(pub_buffer);
Serial.print(": ");
Serial.println(buffer);
blink_data_led(50);
}
void reconnect() {
// Loop until we're reconnected
if (!CloudClient.connected()) {
cloudmillis = millis();
// Serial.print(F("Username: "));
// Serial.println(CLOUD_USERNAME);
Serial.println("Connecting to cloud server...");
// Attempt to connect
//Serial.println(sub_buffer);
if (CloudClient.connect(clientid, mqtt_username, mqtt_password)) {
Serial.println("Connected");
// Once connected, publish an announcement...
Serial.print("Subscribing to ");
Serial.println(sub_buffer);
CloudClient.subscribe(sub_buffer);
Publish(pub_buffer, "all:0");
ticker.detach();
LED_Color(0, 1023, 1023);
//Serial.swap();
} else {
Serial.print("Failed, rc=");
Serial.print(CloudClient.state());
Serial.println(" try again in 30 seconds");
}
}
}
void ApplyCalibration(byte module, byte type)
{
CustomExpansion[module - 1] = type;
switch (type)
{
case CUSTOM_SALINITY:
sprintf(pub_salinity, "cexp:%d:", module - 1);
break;
case CUSTOM_ORP:
sprintf(pub_orp, "cexp:%d:", module - 1);
break;
case CUSTOM_PHEXP:
sprintf(pub_phexp, "cexp:%d:", module - 1);
break;
case CUSTOM_WL:
sprintf(pub_wl, "cexp:%d:", module - 1);
sprintf(pub_custom_wl, "cexpc:%d:", module - 1);
break;
case CUSTOM_MULTI_WL1:
case CUSTOM_MULTI_WL2:
case CUSTOM_MULTI_WL3:
case CUSTOM_MULTI_WL4:
sprintf(pub_multiwl, "cexp:%d:", module - 1);
sprintf(pub_custom_multiwl, "cexpc:%d:", module - 1);
break;
case CUSTOM_NONE:
break;
}
}
void saveConfigCallback () {
Serial.println("Should save config");
shouldSaveConfig = true;
}
void configModeCallback (WiFiManager *myWiFiManager) {
LED_Color(0, 0, 0);
ticker.detach();
Serial.println("Entered config mode");
WiFi.softAPIP();
ticker.attach(0.5, APtick);
}
void WifiCheckCallback () {
WiFi.softAPdisconnect(true);
LED_Color(0, 0, 0);
ticker.detach();
Serial.println("Checking Wifi Credentials");
ticker.attach(0.5, Wifitick);
}
void callback(char* topic, byte* payload, unsigned int length) {
boolean data_found = false;
int payload_data = 0;
byte data_index = 0;
Serial.print("cloud:");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
if (payload[0] == 'R' && payload[1] == 'O' && payload[2] == 'F' && payload[3] == 'F' && payload[5] == ':')
{
data_found = true;
data_index = payload[4] - '0' - 1;
for (byte a = 6; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
RelayMaskOffE[data_index] = payload_data;
}
else if (payload[0] == 'R' && payload[1] == 'O' && payload[2] == 'N' && payload[4] == ':')
{
data_found = true;
data_index = payload[3] - '0' - 1;
for (byte a = 5; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
RelayMaskOnE[data_index] = payload_data;
}
else if (payload[0] == 'R' && payload[2] == ':')
{
data_found = true;
data_index = payload[1] - '0' - 1;
for (byte a = 3; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
RelayDataE[data_index] = payload_data;
}
else if (payload[0] == 'P' && payload[1] == 'W' && payload[2] == 'M' && payload[3] == 'E' && payload[5] == ':')
{
data_found = true;
data_index = payload[4] - '0';
for (byte a = 6; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
ExpansionChannel[data_index] = payload_data;
}
else if (payload[0] == 'P' && payload[1] == 'W' && payload[2] == 'M' && payload[3] == 'E' && payload[5] == 'O' && payload[6] == ':')
{
data_found = true;
data_index = payload[4] - '0';
for (byte a = 7; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
ExpansionChannelOverride[data_index] = payload_data;
}
else if (payload[0] == 'P' && payload[1] == 'W' && payload[2] == 'M' && payload[3] == '1' && payload[4] == '6' && payload[5] == 'E' && payload[8] == ':')
{
data_found = true;
data_index = (payload[6] - '0') * 10;
data_index += (payload[7] - '0');
for (byte a = 9; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
Expansion16Channel[data_index] = payload_data;
}
else if (payload[0] == 'P' && payload[1] == 'W' && payload[2] == 'M' && payload[3] == '1' && payload[4] == '6' && payload[5] == 'E' && payload[6] == 'O' && payload[9] == ':')
{
data_found = true;
data_index = (payload[7] - '0') * 10;
data_index += (payload[8] - '0');
for (byte a = 10; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
Expansion16ChannelOverride[data_index] = payload_data;
}
else if (payload[0] == 'O' && payload[1] == 'R' && payload[2] == 'P' && payload[3] == 'C' && payload[4] == ':' && ORPFound)
{
Serial.println(F("ORP calibration"));
data_found = true;
if (payload[5] == '0')
ORPCal = false;
else if (payload[5] == '1')
ORPCal = true;
else if (payload[5] == '2')
{
for (byte a = 7; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
CalVal1 = payload_data;
}
else if (payload[5] == '3')
{
for (byte a = 7; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
memory_write_int(Mem_I_ORPMin, CalVal1);
memory_write_int(Mem_I_ORPMax, payload_data);
ORPMin = CalVal1;
ORPMax = payload_data;
ORPCal = false;
}
}
else if (payload[0] == 'S' && payload[1] == 'A' && payload[2] == 'L' && payload[3] == 'C' && payload[4] == ':' && SalinityFound)
{
Serial.println(F("Sal calibration"));
data_found = true;
if (payload[5] == '0')
SalCal = false;
else if (payload[5] == '1')
SalCal = true;
else if (payload[5] == '2')
{
for (byte a = 7; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
memory_write_int(Mem_I_SalMax, payload_data);
SalMax = payload_data;
SalCal = false;
}
}
else if (payload[0] == 'P' && payload[1] == 'H' && payload[2] == 'E' && payload[3] == 'C' && payload[4] == ':' && PHExpFound)
{
Serial.println(F("PH Exp calibration"));
data_found = true;
if (payload[5] == '0')
PHExpCal = false;
else if (payload[5] == '1')
PHExpCal = true;
else if (payload[5] == '2')
{
for (byte a = 7; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
CalVal1 = payload_data;
}
else if (payload[5] == '3')
{
for (byte a = 7; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
memory_write_int(Mem_I_PHExpMin, CalVal1);
memory_write_int(Mem_I_PHExpMax, payload_data);
PHExpMin = CalVal1;
PHExpMax = payload_data;
PHExpCal = false;
}
}
else if (payload[0] == 'W' && payload[1] == 'L' && payload[3] == 'C' && payload[4] == ':' && (WLFound || MultiWLFound))
{
Serial.println(F("WL calibration"));
data_found = true;
data_index = payload[2] - '0';
if (payload[5] == '0')
WLCal[data_index] = false;
else if (payload[5] == '1')
{
if (data_index == 0 && WLFound)
WLCal[data_index] = true;
if (data_index > 0 && MultiWLFound)
WLCal[data_index] = true;
}
else if (payload[5] == '2')
{
for (byte a = 7; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
CalVal1 = payload_data;
}
else if (payload[5] == '3')
{
for (byte a = 7; a < length; a++)
{
if (payload[a] != 10 && payload[a] != 13)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
}
if (data_index == 0)
{
memory_write_int(Mem_I_WaterLevelMin, CalVal1);
memory_write_int(Mem_I_WaterLevelMax, payload_data);
}
if (data_index > 0 && data_index < 5)
{
memory_write_int(Mem_I_WaterLevel1Min + (data_index * 2) - 2, CalVal1);
memory_write_int(Mem_I_WaterLevel1Max + (data_index * 2) - 2, payload_data);
}
WLMin[data_index] = CalVal1;
WLMax[data_index] = payload_data;
WLCal[data_index] = false;
}
}
else if (payload[0] == 'C' && payload[1] == 'E' && payload[2] == 'X' && payload[3] == 'P' && payload[5] == 'C' && payload[6] == ':')
{
Serial.println(F("Custom Exp calibration"));
data_found = true;
data_index = payload[4] - '0';
if (payload[7] == '0')
{
if (pub_salinity[0] == 'c' && CustomExpansion[data_index] == CUSTOM_SALINITY) SalCal = false;
if (pub_orp[0] == 'c' && CustomExpansion[data_index] == CUSTOM_ORP) ORPCal = false;
if (pub_phexp[0] == 'c' && CustomExpansion[data_index] == CUSTOM_PHEXP) PHExpCal = false;
if (pub_wl[0] == 'c' && CustomExpansion[data_index] == CUSTOM_WL) WLCal[0] = false;
for (int a = 1; a < WL_CHANNELS; a++)
if (pub_multiwl[0] == 'c' && CustomExpansion[data_index] >= CUSTOM_MULTI_WL1 && CustomExpansion[data_index] <= CUSTOM_MULTI_WL4) WLCal[a] = false;
}
else if (payload[7] == '1')
{
if (pub_salinity[0] == 'c' && CustomExpansion[data_index] == CUSTOM_SALINITY) SalCal = true;
if (pub_orp[0] == 'c' && CustomExpansion[data_index] == CUSTOM_ORP) ORPCal = true;
if (pub_phexp[0] == 'c' && CustomExpansion[data_index] == CUSTOM_PHEXP) PHExpCal = true;
if (pub_wl[0] == 'c' && CustomExpansion[data_index] == CUSTOM_WL) WLCal[0] = true;
if (pub_multiwl[0] == 'c' && CustomExpansion[data_index] == CUSTOM_MULTI_WL1) WLCal[1] = true;
if (pub_multiwl[0] == 'c' && CustomExpansion[data_index] == CUSTOM_MULTI_WL2) WLCal[2] = true;
if (pub_multiwl[0] == 'c' && CustomExpansion[data_index] == CUSTOM_MULTI_WL3) WLCal[3] = true;
if (pub_multiwl[0] == 'c' && CustomExpansion[data_index] == CUSTOM_MULTI_WL4) WLCal[4] = true;
}
else if (payload[7] == '2')
{
for (byte a = 9; a < length; a++)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
CalVal1 = payload_data;
}
else if (payload[7] == '3')
{
for (byte a = 9; a < length; a++)
{
payload_data *= 10;
payload_data += payload[a] - '0';
}
if (pub_salinity[0] == 'c')
{
memory_write_int(Mem_I_SalMax, payload_data);
SalMax = payload_data;
SalCal = false;
}
if (pub_orp[0] == 'c')
{
memory_write_int(Mem_I_ORPMin, CalVal1);
memory_write_int(Mem_I_ORPMax, payload_data);
ORPMin = CalVal1;
ORPMax = payload_data;
ORPCal = false;
}
if (pub_phexp[0] == 'c')
{
memory_write_int(Mem_I_PHExpMin, CalVal1);
memory_write_int(Mem_I_PHExpMax, payload_data);
PHExpMin = CalVal1;
PHExpMax = payload_data;
PHExpCal = false;
}
if (pub_wl[0] == 'c')
{
memory_write_int(Mem_I_WaterLevelMin, CalVal1);
memory_write_int(Mem_I_WaterLevelMax, payload_data);
WLMin[0] = CalVal1;
WLMax[0] = payload_data;
WLCal[0] = false;
}
for (int a = 1; a < WL_CHANNELS; a++)
if (pub_multiwl[0] == 'c' && WLCal[a] == true)
{
memory_write_int(Mem_I_WaterLevel1Min + (a * 2) - 2, CalVal1);
memory_write_int(Mem_I_WaterLevel1Max + (a * 2) - 2, payload_data);
WLMin[a] = CalVal1;
WLMax[a] = payload_data;
WLCal[a] = false;
}
}
}
if (data_found)
{
blink_data_led(30);
for (int a = 0; a < length; a++)
Serial.write(payload[a]);
Serial.println();
}
}
void setup() {
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
pinMode(LED_RED, OUTPUT);
pinMode(INPUT1, INPUT_PULLUP);
pinMode(INPUT2, INPUT_PULLUP);
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_BLUE, HIGH);
digitalWrite(LED_RED, HIGH);
randomSeed(analogRead(0));
wifi_station_set_hostname( "ReefAngelCloudHub" );
Serial.begin(57600);
Serial.setRxBufferSize(2048);
EEPROM.begin(512);
Wire.begin();
wifiManager.setDebugOutput(true);
Serial.println("");
Serial.print("Current version: ");
Serial.println(version);
SalinityFound = false;
ORPFound = false;
PHExpFound = false;
WLFound = false;
MultiWLFound = false;
HumidityFound = false;
sprintf(pub_salinity, "sal");
sprintf(pub_orp, "orp");
sprintf(pub_phexp, "phe");
sprintf(pub_wl, "wl");
sprintf(pub_multiwl, "wl");
sprintf(pub_humidity, "hum");
// Temperature
byte count = 0;
while (ds.search(addr))
{
if (addr[0] == 0x28)
{
count++;
if (count == 1) memcpy(addr1, addr, 8);
if (count == 2) memcpy(addr2, addr, 8);
}
}
ds.reset_search();
Serial.print(count);
Serial.println(" temperature probe(s) found");
// Relay
for ( byte EID = 0; EID < MAX_RELAY_EXPANSION_MODULES; EID++ )
{
RelayDataE[EID] = 0;
RelayMaskOnE[EID] = 0;
RelayMaskOffE[EID] = 0xff;
}
RelayFallBackE[0] = Relay1FallBack;
RelayFallBackE[1] = Relay2FallBack;
RelayFallBackE[2] = Relay3FallBack;
RelayFallBackE[3] = Relay4FallBack;
RelayFallBackE[4] = Relay5FallBack;
RelayFallBackE[5] = Relay6FallBack;
RelayFallBackE[6] = Relay7FallBack;
RelayFallBackE[7] = Relay8FallBack;
// Dimming
lastcrc = -1;
for ( byte a = 0; a < PWM_EXPANSION_CHANNELS; a++ )
{
ExpansionChannel[a] = 0;
ExpansionChannelOverride[a] = 255;
}
// 16 Channel Dimming
lastcrc16 = -1;
for ( byte a = 0; a < PWM16_EXPANSION_CHANNELS; a++ )
{
Expansion16Channel[a] = 0;
Expansion16ChannelOverride[a] = 255;
}
// IO
Params.IO = 63;
OldParams.IO = 63;
// Leak
Params.Leak = 0;
OldParams.Leak = 0;
// ORP
Params.ORP = 0;
OldParams.ORP = 0;
ORPCal = false;
ORPMin = memory_read_int(Mem_I_ORPMin);
if (ORPMin == 65535) ORPMin = 0;
ORPMax = memory_read_int(Mem_I_ORPMax);
// Salinity
Params.Salinity = 0;
OldParams.Salinity = 0;
SalCal = false;
SalMax = memory_read_int(Mem_I_SalMax);
// pH Exp
Params.PHExp = 0;
OldParams.PHExp = 0;
PHExpCal = false;
PHExpMin = memory_read_int(Mem_I_PHExpMin);
if (PHExpMin == 65535) PHExpMin = 0;
PHExpMax = memory_read_int(Mem_I_PHExpMax);
// Humidity
Params.Humidity = 0;
OldParams.Humidity = 0;
// Water Level
for (int a = 0; a < WL_CHANNELS; a++)
{
Params.WL[a] = 0;
OldParams.WL[a] = 0;
WLCal[a] = false;
}
WLMin[0] = memory_read_int(Mem_I_WaterLevelMin);
WLMax[0] = memory_read_int(Mem_I_WaterLevelMax);
WLMin[1] = memory_read_int(Mem_I_WaterLevel1Min);
WLMax[1] = memory_read_int(Mem_I_WaterLevel1Max);
WLMin[2] = memory_read_int(Mem_I_WaterLevel2Min);
WLMax[2] = memory_read_int(Mem_I_WaterLevel2Max);
WLMin[3] = memory_read_int(Mem_I_WaterLevel3Min);
WLMax[3] = memory_read_int(Mem_I_WaterLevel3Max);
WLMin[4] = memory_read_int(Mem_I_WaterLevel4Min);
WLMax[4] = memory_read_int(Mem_I_WaterLevel4Max);
for (int a = 0; a < 5; a++)
{
if (WLMin[a] == 65535) WLMin[a] = 0;
}
ApplyCalibration(1, CloudCustomModule1);
ApplyCalibration(2, CloudCustomModule2);
ApplyCalibration(3, CloudCustomModule3);
ApplyCalibration(4, CloudCustomModule4);
ApplyCalibration(5, CloudCustomModule5);
ApplyCalibration(6, CloudCustomModule6);
ApplyCalibration(7, CloudCustomModule7);
ApplyCalibration(8, CloudCustomModule8);
if (analogRead(RESET_ALL) < 50)
{
// updating=true;
Serial.println("Resetting");
EEPROM.write(128, 0);
EEPROM.commit();
ESP.reset();
while (1);
}
wifiManager.setVersion(version);
if (EEPROM.read(128) != 1)
{
wifiManager.resetSettings();
SPIFFS.format();
EEPROM.write(128, 1);
EEPROM.commit();
}
if (SPIFFS.begin()) {
Serial.println("mounted file system");
if (SPIFFS.exists("/config.json")) {
//file exists, reading and loading
Serial.println("reading config file");
File configFile = SPIFFS.open("/config.json", "r");
if (configFile) {
Serial.println("opened config file");
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.parseObject(buf.get());
json.printTo(Serial);
if (json.success()) {
Serial.println("\nparsed json");
strcpy(mqtt_username, json["mqtt_username"]);
strcpy(mqtt_password, json["mqtt_password"]);
} else {
Serial.println("failed to load json config");
}
}
}
} else {
Serial.println("failed to mount FS");
}
WiFiManagerParameter custom_mqtt_username("Cloud Username", "Cloud Username", mqtt_username, 40);
WiFiManagerParameter custom_mqtt_password("Cloud Password", "Cloud Password", mqtt_password, 40);
wifiManager.addParameter(&custom_mqtt_username);
wifiManager.addParameter(&custom_mqtt_password);
wifiManager.setSaveConfigCallback(saveConfigCallback);
wifiManager.setAPCallback(configModeCallback);
wifiManager.setCheckWifiCallback(WifiCheckCallback);
LED_Color(0, 0, 0);
ticker.attach(0.5, Wifitick);
wifiManager.autoConnect("ReefAngelCloudHub");
ticker.detach();
LED_Color(900, 0, 1023);
CloudClient.setServer(MQTTServer, MQTTPORT);
CloudClient.setCallback(callback);
strcpy(mqtt_username, custom_mqtt_username.getValue());
strcpy(mqtt_password, custom_mqtt_password.getValue());
Serial.print("username: ");
Serial.println(mqtt_username);
// Serial.print("password: ");
// Serial.println(mqtt_password);
sprintf(clientid, "%s%02d", mqtt_username, random(10000));
sprintf(pub_buffer, "%s/in", mqtt_username);
sprintf(sub_buffer, "%s/out/#", mqtt_username);
Serial.println("Start");
if (shouldSaveConfig) {
Serial.println("saving config");
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
json["mqtt_username"] = mqtt_username;
json["mqtt_password"] = mqtt_password;
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("failed to open config file for writing");
}
json.printTo(Serial);
Serial.println("");
json.printTo(configFile);
configFile.close();
//end save
}
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void loop() {
if ((analogRead(RESET_ALL) < 50) && !updating)
{
if (WiFi.status() == WL_CONNECTED)
{
updating = true;
ticker.detach();
LED_Color(1023, 900, 0);
Serial.println("Updating...");
ESPhttpUpdate.update("forum.reefangel.com", 80, "/firmware/CloudWifiESP8266Hub.ino.bin");
}
else
{
Serial.println("Resetting");
EEPROM.write(128, 0);
EEPROM.commit();
ESP.reset();
while (1);
}
}
if (!CloudClient.connected() && (((millis() - cloudmillis) > 30000) || first_connection)) {
first_connection = false;
LED_Color(0, 0, 0);
ticker.detach();
ticker.attach(0.5, Cloudtick);
reconnect();
}
CloudClient.loop();
// Relay
for ( byte EID = 0; EID < MAX_RELAY_EXPANSION_MODULES; EID++ )
{
byte TempRelay = RelayDataE[EID];
TempRelay &= RelayMaskOffE[EID];
TempRelay |= RelayMaskOnE[EID];
if (!CloudClient.connected())
TempRelay = RelayFallBackE[EID];
Wire.beginTransmission(I2CExpModule + EID);
Wire.write(~TempRelay); // MSB
Wire.endTransmission();
}
// Dimming
byte thiscrc = 0;
for ( byte a = 0; a < PWM_EXPANSION_CHANNELS; a++ )
{
thiscrc += ExpansionChannel[a] * (a + 1);
thiscrc += ExpansionChannelOverride[a] * (a + 1);
}
if (millis() % 60000 < 200) lastcrc = -1;
if (lastcrc != thiscrc || millis() < 5000)
{
lastcrc = thiscrc;
// setup PCA9685 for data receive
// we need this to make sure it will work if connected ofter controller is booted, so we need to send it all the time.
Wire.beginTransmission(I2CPWM_PCA9685);
Wire.write((uint8_t)0);
Wire.write(0xa1);
Wire.endTransmission();
for ( byte a = 0; a < PWM_EXPANSION_CHANNELS; a++ )
{
int data;
if (ExpansionChannelOverride[a] <= 100)
data = ExpansionChannelOverride[a] * 40.96;
else
data = ExpansionChannel[a] * 40.96;
Wire.beginTransmission(I2CPWM_PCA9685);
Wire.write(0x8 + (4 * a));
Wire.write(data & 0xff);
Wire.write(data >> 8);
Wire.endTransmission();
}
}
// 16 Channel Dimming
int thiscrc16 = 0;
for ( byte a = 0; a < PWM16_EXPANSION_CHANNELS; a++ )
{
thiscrc16 += Expansion16Channel[a] * (a + 1);
thiscrc16 += Expansion16ChannelOverride[a] * (a + 1);
}
if (millis() % 60000 < 200) lastcrc16 = -1;
if (lastcrc16 != thiscrc16 || millis() < 5000)
{
lastcrc16 = thiscrc16;
// setup PCA9685 for data receive
// we need this to make sure it will work if connected ofter controller is booted, so we need to send it all the time.
Wire.beginTransmission(I2CPWM_16CH_PCA9685);
Wire.write((uint8_t)0);
Wire.write(0xa1);
Wire.endTransmission();
for ( byte a = 0; a < PWM16_EXPANSION_CHANNELS; a++ )
{
int data;
if (Expansion16ChannelOverride[a] <= 100)
data = Expansion16ChannelOverride[a];
else
data = Expansion16Channel[a];
Wire.beginTransmission(I2CPWM_16CH_PCA9685);
Wire.write(0x8 + (4 * a));
Wire.write(data & 0xff);
Wire.write(data >> 8);
Wire.endTransmission();
}
}
if (CloudClient.connected())
{
// IO
Params.IO = 63;
Wire.requestFrom(I2CIO_PCF8574, 1);
if (Wire.available())
Params.IO = Wire.read();
if (digitalRead(INPUT1) == HIGH) Params.IO += 64;
if (digitalRead(INPUT2) == HIGH) Params.IO += 128;
if (Params.IO != OldParams.IO)
{
OldParams.IO = Params.IO;
sprintf(buffer, "io:%d", Params.IO);
Publish(pub_buffer, buffer);
}
// Leak
int iLeak = 0;
Wire.requestFrom(I2CLeak, 2);
if (Wire.available())
{
iLeak = Wire.read();
iLeak = iLeak << 8;
iLeak += Wire.read();
Params.Leak = iLeak > 2000;
}
if (Params.Leak != OldParams.Leak)
{
OldParams.Leak = Params.Leak;
sprintf(buffer, "leak:%d", Params.Leak);
Publish(pub_buffer, buffer);
}
if (millis() - Paramsmillis > 1000)
{
Paramsmillis = millis();
// Temperature
if (addr1[0] != 0)
{
ds.reset();
ds.select(addr1);
ds.write(0xBE); // Read Scratchpad
for (byte i = 0; i < 2; i++)
{
data[i] = ds.read();
}
Params.Temp[1] = (data[1] << 8) + data[0]; //take the two bytes from the response relating to temperature
Params.Temp[1] = Params.Temp[1] / 1.6;
if (Params.Temp[1] == 0) Params.Temp[1] = 0;
if (Params.Temp[1] > 850) Params.Temp[1] = 0;
Params.Temp[1] = Params.Temp[1] * 1.8 + 320;
if (Params.Temp[1] != OldParams.Temp[1])
{
OldParams.Temp[1] = Params.Temp[1];
sprintf(buffer, "t:%d:%d", TempProbeIDs[0], Params.Temp[1]);
if (TempProbeIDs[0] != 0) Publish(pub_buffer, buffer);
}
}
if (addr2[0] != 0)
{
ds.reset();
ds.select(addr2);
ds.write(0xBE); // Read Scratchpad
for (byte i = 0; i < 2; i++)
{
data[i] = ds.read();
}
Params.Temp[2] = (data[1] << 8) + data[0]; //take the two bytes from the response relating to temperature
Params.Temp[2] = Params.Temp[2] / 1.6;
if (Params.Temp[2] == 0) Params.Temp[2] = 0;
if (Params.Temp[2] > 850) Params.Temp[2] = 0;
Params.Temp[2] = Params.Temp[2] * 1.8 + 320;
if (Params.Temp[2] != OldParams.Temp[2])
{
OldParams.Temp[2] = Params.Temp[2];
sprintf(buffer, "t:%d:%d", TempProbeIDs[1], Params.Temp[2]);
if (TempProbeIDs[1] != 0) Publish(pub_buffer, buffer);
}
}
// ORP
unsigned long temporp = 0;
int iORP = 0;
for (int a = 0; a < 20; a++)
{
Wire.requestFrom(I2CORP, 2);
if (Wire.available())
{
iORP = Wire.read();
iORP = iORP << 8;
iORP += Wire.read();
}
temporp += iORP;
}
temporp = temporp / 20;
if (temporp != 0)
{
Params.ORP = map(temporp, ORPMin, ORPMax, 0, 470); // apply the calibration to the sensor reading
Params.ORP = constrain(Params.ORP, 0, 550);
if (pub_orp[0] == 'o') ORPFound = true;
}
if (ORPCal)
{
sprintf(buffer, "%sc:%d", pub_orp, temporp);
Publish(pub_buffer, buffer);
}
else
{
if (Params.ORP != OldParams.ORP)
{
OldParams.ORP = Params.ORP;
sprintf(buffer, "%s:%d", pub_orp, Params.ORP);
Publish(pub_buffer, buffer);
}
}
// Salinity
unsigned long tempsal = 0;
int iSAL = 0;
for (int a = 0; a < 20; a++)
{
Wire.requestFrom(I2CSalinity, 2);
if (Wire.available())
{
iSAL = Wire.read();
iSAL = iSAL << 8;
iSAL += Wire.read();
}
tempsal += iSAL;
}
tempsal = tempsal / 20;
if (tempsal != 0)
{
Params.Salinity = map(tempsal, 0, SalMax, 60, 350); // apply the calibration to the sensor reading
if (pub_salinity[0] == 's') SalinityFound = true;
}
if (SalCal)
{
sprintf(buffer, "%sc:%d", pub_salinity, tempsal);
Publish(pub_buffer, buffer);
}
else
{
if (Params.Salinity != OldParams.Salinity)
{
OldParams.Salinity = Params.Salinity;
sprintf(buffer, "%s:%d", pub_salinity, Params.Salinity);
Publish(pub_buffer, buffer);
}
}
// pH Exp
unsigned long tempphexp = 0;
int iPHExp = 0;
for (int a = 0; a < 20; a++)
{
Wire.requestFrom(I2CPH, 2);
if (Wire.available())
{
iPHExp = Wire.read();
iPHExp = iPHExp << 8;
iPHExp += Wire.read();
}
tempphexp += iPHExp;
}
tempphexp = tempphexp / 20;
if (tempphexp != 0)
{
Params.PHExp = map(tempphexp, PHExpMin, PHExpMax, 700, 1000); // apply the calibration to the sensor reading
Params.PHExp = constrain(Params.PHExp, 100, 1400);
if (pub_phexp[0] == 'p') PHExpFound = true;
}
if (PHExpCal)
{
sprintf(buffer, "%sc:%d", pub_phexp, tempphexp);
Publish(pub_buffer, buffer);
}
else
{
if (Params.PHExp != OldParams.PHExp)
{
OldParams.PHExp = Params.PHExp;
sprintf(buffer, "%s:%d", pub_phexp, Params.PHExp);
Publish(pub_buffer, buffer);
}
}
// PAR
unsigned long temppar = 0;
int iPAR = 0;
for (int a = 0; a < 20; a++)
{
Wire.requestFrom(I2CPAR, 2);
if (Wire.available())
{
iPAR = Wire.read();
iPAR = iPAR << 8;
iPAR += Wire.read();
}
temppar += iPAR;
}
temppar = temppar / 20;
if (temppar != 0)
{
temppar *= 5000; // apply the calibration to the sensor reading
temppar /= 8192;
Params.PAR = temppar;
}
if (Params.PAR != OldParams.PAR)
{
OldParams.PAR = Params.PAR;
sprintf(buffer, "par:%d", Params.PAR);
Publish(pub_buffer, buffer);
}
// Humidity
int iHumidity = 0;
int reply[10];
Wire.beginTransmission(I2CHumidity);
Wire.endTransmission(); // For some reason, it needs this to work??? Bug with sensor??
Wire.beginTransmission(I2CHumidity);
Wire.write(0x3); // 0x3 for read 0x10 for write to registers
Wire.write((byte)0x0); // start at address 0x0 for humidity
Wire.write(2); // request 2 bytes data for humidity or 4 bytes for temperature+humidity
Wire.endTransmission();
Wire.requestFrom(I2CHumidity, 6); // Request 6 bytes
for (int i = 0; i < 6; i++)
if (Wire.available()) reply[i] = Wire.read();
if (reply[0] == 0x3 && reply[1] == 0x2) // The response need to contain function (0x3) and length of data (0x2)
{
int crc = reply[5];
crc <<= 8;
crc += reply[4];
if (crc == crc16(reply, 4))
{
iHumidity = reply[2];
iHumidity <<= 8;
iHumidity += reply[3];
}
}
Params.Humidity = iHumidity;
if (Params.Humidity != OldParams.Humidity)
{
OldParams.Humidity = Params.Humidity;
sprintf(buffer, "hum:%d", Params.Humidity);
Publish(pub_buffer, buffer);
}
// Water Level
for (int i = 0; i < WL_CHANNELS; i++)
{
unsigned long tempwl = 0;
int iWL = 0;
for (int a = 0; a < 20; a++)
{
if (i == 0)
{
Wire.requestFrom(I2CWaterLevel, 2);
if (Wire.available())
{
iWL = Wire.read();
iWL = iWL << 8;
iWL += Wire.read();
}
tempwl += iWL;
if (tempwl != 0 && pub_wl[0] == 'w') WLFound = true;
}
else
{
Wire.beginTransmission(I2CMultiWaterLevel);
Wire.write(1); // Config Pointer
byte addr = (0xb + i) << 4; // Select which channel to read
addr += 0x03; // Programmable Gain
Wire.write(addr);
Wire.write(0x83);
Wire.endTransmission();
delay(10); // It takes 10ms for conversion to be completed
Wire.beginTransmission(I2CMultiWaterLevel);
Wire.write((uint8_t)0); // Convert Pointer
Wire.endTransmission();
Wire.requestFrom(I2CMultiWaterLevel, 2); // Request converted value
if (Wire.available())
{
iWL = Wire.read();
iWL = iWL << 8;
iWL += Wire.read();
}
tempwl += iWL >> 4;
if (tempwl != 0 && pub_multiwl[0] == 'w') MultiWLFound = true;
}
}
tempwl = tempwl / 20;
if (tempwl != 0)
{
Params.WL[i] = map(tempwl, WLMin[i], WLMax[i], 0, 100); // apply the calibration to the sensor reading
Params.WL[i] = constrain(Params.WL[i], 0, 255);
}
if (WLCal[i])
{
if (i == 0)
{
if (pub_wl[0] == 'c')
sprintf(buffer, "%s%d", pub_custom_wl, tempwl);
else
sprintf(buffer, "%sc:%d", pub_wl, tempwl);
}
else
{
if (pub_multiwl[0] == 'c')
sprintf(buffer, "%s%d", pub_custom_multiwl, tempwl);
else
sprintf(buffer, "%sc:%d", pub_multiwl, tempwl);
}
Publish(pub_buffer, buffer);
}
else
{
if (Params.WL[i] != OldParams.WL[i])
{
OldParams.WL[i] = Params.WL[i];
if (i == 0)
{
if (pub_wl[0] == 'c')
sprintf(buffer, "%s%d", pub_wl, Params.WL[i]);
else
sprintf(buffer, "%s:%d:%d", pub_wl, i, Params.WL[i]);
}
else
{
if (pub_multiwl[0] == 'c')
sprintf(buffer, "%s%d", pub_multiwl, Params.WL[i]);
else
sprintf(buffer, "%s:%d:%d", pub_multiwl, i, Params.WL[i]);
}
Publish(pub_buffer, buffer);
}
}
}
ds.reset();
ds.select(addr1);
ds.write(0x44, 0);
ds.reset();
ds.select(addr2);
ds.write(0x44, 0);
}
}
}
This is the code we previously loaded with the 16ch Dimming feature enabled.