I am glad to report that my first attempt at designing a board went well overall. The project/boards linked in my above post works as is, but I would make a few modifications on future boards. Some of the modifications I will hopefully implement soon are:
- Addition of holes so the board can be mounted to a project enclosure.
- Combine the Water/Air Temp sensors onto a single pin to the ATMega328p along with adding a resistor between the data and VCC. Unfortunately, I did not realize that DS18B20's could be chained together to save on pins, or that it needs to have a pull-up resistor.
- Drop the number of I2C pin connections to one or two. In addition to this I will probably also make a simple breakout board for expanding the number of connections as needed.
- Include I2C pull up resistors. It has been functioning without any issues without them, but I should have put them on the board.
The Sketch I have created and have running includes the following sensors so far:
- Atlas Scientific pH sensor and probe
- Atlas Scientific ORP sensor and probe
- (2)Dallas DS18B20 for water and air temp.
- Relay for pool pump
- MPX5500DP pressure sensor to monitor water at the filter pressure
- A 4 line 20 character LCD display currently showing temps/ORP/pH
The sketch is sending the data to my MIOS as expected. However, when I turn the relay on I have noticed the message is broadcast quite a few times. When debugging is active it seemed that the incoming message would display around a hundred times (I never actually counted the number of times). Is such a large number of attempts to send the message normal? Any feedback or recommendations are welcome.
Note: I do not have the arduino bootloader running on my boards. I am using the Visual Micro addin for AtmelStudio and an MKII ISP to program the boards.
#include <SPI.h>
#include <MySensor.h>
#include <Wire.h>
#include <DallasTemperature.h>
#include <OneWire.h>
#include <LiquidCrystal_I2C.h>
/*#### MySensor Node definitions ####*/
#define NODE_ID 2
#define PRES_CHILD 0
#define WTEMP_CHILD 1
#define ATEMP_CHILD 2
#define RELAY_CHILD_ID 3
#define PH_CHILD_ID 4
#define ORP_CHILD_ID 5
/*#### Arduino Pin definitions #####*/
#define MPX5500DP_PIN 0 // A0 - MPX5500DP pin
#define WTEMP_PIN 3 // D3 - Dallas one wire temp sensor
#define ATEMP_PIN 4 // D4 - Dallas one wire temp sensor
#define PUMP_RELAY 6 // D6 - Relay Pin
/*#### Program definitions #####*/
#define PH_I2C 99 //default I2C ID number for EZO pH Circuit.
#define ORP_I2C 98 //default I2C ID number for EZO ORP Circuit.
#define NUMBER_OF_RELAYS 1 // Total number of attached relays
#define RELAY_ON 1 // GPIO value to write to turn on attached relay
#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
#define MAX_ATTACHED_DS18B20 16 // Define the max number of DS18B20 per pin
#define LCD_I2C 0x3F // I2C address of the LCD screen
/*#### Program variables #####*/
unsigned long SLEEP_TIME = 5000; // Sleep time between reads (in mili-seconds)
int delay_count = 0;
int periodUpdate = 0;
LiquidCrystal_I2C lcd(LCD_I2C, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address
//temperature variables
OneWire waterOneWire(WTEMP_PIN);
DallasTemperature wSensors(&waterOneWire);
OneWire airOneWire(ATEMP_PIN);
DallasTemperature aSensors(&airOneWire);
float atemperature = 0;
float wtemperature = 0;
float lastPressure = -1;
float lastWTemp = -1;
float lastATemp = -1;
// &&&& pH and ORP &&&&
byte code=0; //used to hold the I2C response code.
char I2C_data[10]; //we make a 20 byte character array to hold incoming data from the circuit.
byte in_char=0; //used as a 1 byte buffer to store in bound bytes from the pH Circuit.
byte i=0; //counter used for ph_data array.
float ph_float = 0; //float var used to hold the float value of the pH.
float ORP_float = 0; //float var used to hold the float value of the ORP.
float last_ph_float = 1; //float var used to hold the float value of the last pH.
float last_ORP_float = 1; //float var used to hold the float value of the last ORP.
/*#### My Sensors #####*/
boolean metric;
MySensor gw;
//Message definitions
MyMessage pHMsg(PH_CHILD_ID, V_VAR1);
MyMessage orpMsg(ORP_CHILD_ID, V_VAR1);
MyMessage wTempMsg(WTEMP_CHILD, V_TEMP);
MyMessage aTempMsg(ATEMP_CHILD, V_TEMP);
MyMessage pressureMsg(PRES_CHILD, V_PRESSURE);
/*#### Program #####*/
void setup() {
wSensors.begin();
aSensors.begin();
gw.begin(incomingMessage, NODE_ID, true);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Sensor Node AlphaMax", "0.1"); // Update Node name for each node -- Update version number for each update.
// Register sensors to gw (they will be created as child devices)
gw.present(PRES_CHILD, S_BARO);
gw.present(WTEMP_CHILD, S_TEMP);
gw.present(ATEMP_CHILD, S_TEMP);
gw.present(PH_CHILD_ID, S_CUSTOM);
gw.present(ORP_CHILD_ID, S_CUSTOM);
gw.present(RELAY_CHILD_ID, S_LIGHT);
metric = gw.getConfig().isMetric; // getConfig() returns true for metric, false for imperial
pinMode(PUMP_RELAY, OUTPUT); // Then set relay pins in output mode
digitalWrite(PUMP_RELAY, gw.loadState(RELAY_CHILD_ID)?RELAY_ON:RELAY_OFF); // Set relay to last known state (using eeprom storage)
lcd.begin(20,4); // Initialize the lcd for 20 chars 4 lines
lcd.clear(); // Clear LCD screen before use
}
void loop() {
gw.process();
//Get temperature
aSensors.requestTemperatures();
atemperature = static_cast<float>(static_cast<int>((gw.getConfig().isMetric?aSensors.getTempCByIndex(0):aSensors.getTempFByIndex(0)) * 100.)) / 100.;
wSensors.requestTemperatures();
wtemperature = static_cast<float>(static_cast<int>((gw.getConfig().isMetric?wSensors.getTempCByIndex(0):wSensors.getTempFByIndex(0)) * 100.)) / 100.;
//Get pressure
float pressure = analogRead(MPX5500DP_PIN); //MPX5500DP Pressure sensor
//Get ph and ORP
atlas(PH_I2C);
atlas(ORP_I2C);
//Update LCD Screen
printscreen();
//Update VERA when needed.
if (ph_float != last_ph_float)
{
gw.send(pHMsg.set(ph_float,2));
last_ph_float = ph_float;
}
if (ORP_float != last_ORP_float)
{
gw.send(orpMsg.set(ORP_float,1));
last_ORP_float = ORP_float;
}
//Periodic updates were used when LCD display was on another node
//since they do not appear to be hurting anything I left them in.
if (wtemperature != lastWTemp | periodUpdate == 250000) {
gw.send(wTempMsg.setDestination(0).set(wtemperature,1));
lastWTemp = wtemperature;
}
if (atemperature != lastATemp | periodUpdate == 500000) {
gw.send(aTempMsg.setDestination(0).set(atemperature,1));
lastATemp = atemperature;
}
if (pressure != lastPressure | periodUpdate == 750000) {
gw.send(pressureMsg.set(pressure, 0));
lastPressure = pressure;
}
if(periodUpdate >= 750000)
{
periodUpdate = 0;
}
periodUpdate++;
}
//This function is used to handle data to and from the ORP and pH sensors
void atlas(int address){
Wire.beginTransmission(address); //call the circuit by its ID number.
Wire.write('R'); //transmit the command that was sent through the serial port.
Wire.endTransmission(); //end the I2C data transmission.
for(int k=0;k<=140;k++) // Need to delay 1.4 seconds between sending read command
{ // and requesting data. This for loop keeps delays between
gw.process(); // processing incoming messages short.
delay(10);
}
Wire.requestFrom(address,20,1); //call the circuit and request 20 bytes (this may be more then we need).
code=Wire.read(); //the first byte is the response code, we read this separately.
switch (code){ //switch case based on what the response code is.
case 1: //decimal 1.
Serial.println("Success"); //means the command was successful.
break; //exits the switch case.
case 2: //decimal 2.
Serial.println("Failed"); //means the command has failed.
break; //exits the switch case.
case 254: //decimal 254.
Serial.println("Pending"); //means the command has not yet been finished calculating.
break; //exits the switch case.
case 255: //decimal 255.
Serial.println("No Data"); //means there is no further data to send.
break; //exits the switch case.
}
while(Wire.available()){ //are there bytes to receive.
in_char = Wire.read(); //receive a byte.
I2C_data[i]= in_char; //load this byte into our array.
i+=1; //incur the counter for the array element.
if(in_char==0){ //if we see that we have been sent a null command.
i=0; //reset the counter i to 0.
Wire.endTransmission(); //end the I2C data transmission.
break; //exit the while loop.
}
}
Serial.println(I2C_data); //print the data.
if(address == PH_I2C)
{
ph_float = atof(I2C_data);
}
else if(address == ORP_I2C)
{
ORP_float = atof(I2C_data);
}
else
Serial.print("No address match!!!!");
}
void incomingMessage(const MyMessage &message)
{
// We only expect one type of message from controller. But we better check anyway.
if (message.type==V_LIGHT)
{
// Change relay state
digitalWrite(PUMP_RELAY, message.getBool()?RELAY_ON:RELAY_OFF);
// Store state in eeprom
gw.saveState(RELAY_CHILD_ID, message.getBool());
// Write some debug info
Serial.print("Incoming change for sensor:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(message.getBool());
}
}
void printscreen()
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Water Temp ");
lcd.setCursor(11,0);
lcd.print(wtemperature);
lcd.setCursor(0,1);
lcd.print("Air Temp");
lcd.setCursor(11,1);
lcd.print(atemperature);
lcd.setCursor(0,2);
lcd.print("pH");
lcd.setCursor(11,2);
lcd.print(ph_float);
lcd.setCursor(0,3);
lcd.print("ORP");
lcd.setCursor(11,3);
lcd.print(ORP_float);
}