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);
}