SoftwareSerial and Ethernet (UDP) 'hang' [solved]

My eventual goal of this project is to store real-time(ish) energy usage data for graphing purposes. So far I’ve got most of the individual elements of the program down.

  1. read out the P1 port of a Dutch ‘smart’ meter. Works quite well.
  2. get time data using the ‘time’ library http://playground.arduino.cc/Code/time. Works amazingly easy.
  3. store the data somewhere (haven’t decided on SD or FTP)

While both elements 1 and 2 work brilliantly apart from each other, when I combine them, something weird happens. I think that there’s some conflict between some functions of the SoftwareSerial library and the Ethernet library. Right now, I’ve ‘commented’ five lines in setup(), and one in loop(), and it works. However, when I uncomment the first line:

Ethernet.begin(mac);

The serial connection to the P1 port of the meter is never available; it’s always 0.

Does anyone understand what’s going wrong here? Are these two libraries incompatible or something?

The code:

#include <SPI.h> // just for SD, I think; Ethernet NTP sync example works without this one.
#include <SoftwareSerial.h>
#include <Time.h> // time sync
#include <Ethernet.h> // for NTP service
#include <EthernetUdp.h>

// P1
const byte requestPin = 3; // request pin
int incomingByte = 0;
String inputString = ""; // the reading buffer
unsigned int typeIndex[5]; // index of the type identifier
const char startDelim = '('; // values start with this symbol
const char endDelims[5] = {'*','*',')','*',')'}; // values end with this symbol

// Ethernet
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 
unsigned int localPort = 8888;  // local port to listen for UDP packets

// NTP
IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
const int timeZone = 1; // no DST

// general
String vals; // value storage

// start crap
SoftwareSerial p_one(10, 11, true); // RX, TX, inverted
EthernetUDP Udp;
 
void setup () {
	p_one.begin(9600);
	delay(10);
	Serial.begin(9600);
	pinMode(requestPin, OUTPUT);
	digitalWrite(requestPin, HIGH);
	
	//Ethernet.begin(mac); // if I uncomment this line, p_one.available() starts out with a full buffer, but is zero from then on
	//Serial.print("IP assigned: "); Serial.println(Ethernet.localIP());
	//Udp.begin(localPort);
	//setSyncProvider(getNtpTime);
	//while(timeStatus() == timeNotSet); // wait until time sync

}

void loop () {
	while (p_one.available()) {
		incomingByte = p_one.read();
		incomingByte &= ~(1 << 7);
		char inChar = (char)incomingByte;
		inputString += inChar;
		
		if (inChar == '!'){ // data dump end symbol
			Serial.println(inputString);
			if (inputString.length() == 453) { // data dump must be a specific size. 
				//storeTime();
				findIndices();
				findVals();
				storeVals();
				vals = "";
			}
			inputString = ""; // reset the inputString for the next reading
		}
	}
}

void storeVals(){
// will store the values to either the SD card, or an FTP server. Whichever I get to work first.
}

void findIndices(){
	// finds the indices of the headers
	typeIndex[0] = inputString.indexOf("1.8.1")+4;
	typeIndex[1] = inputString.indexOf("1.8.2")+4;
	typeIndex[2] = inputString.indexOf("96.14.0")+4;
	typeIndex[3] = inputString.indexOf("1.7.0")+4;
	typeIndex[4] = inputString.indexOf("24.2.1")+11;
}

void findVals(){
	// find the actual values, and puts them in 'vals'
	unsigned int indices[2];
	for(byte x = 0; x < 5; x++){
		indices[1] = inputString.indexOf(startDelim,typeIndex[x])+1; // find value location
		indices[2] = inputString.indexOf(endDelims[x],typeIndex[x]); // place value in array
		vals += inputString.substring(indices[1], indices[2]);
		if(x < 4){vals += ",";}
	}
	Serial.println("Vals: "+vals+"\n");
}

void storeTime(){
	// the time library should automatically sync the time
	vals += String(hour())+':';
	if (minute()<10) vals += '0';
	vals += String(minute())+':';
	if (second()<10) vals += '0';
	vals += String(second());
	vals += ",";
	vals += String(day())+'/'+String(month())+'/'+String(year());
}

/*-------- NTP code ----------*/
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets

time_t getNtpTime(){
	while (Udp.parsePacket() > 0) ; // discard any previously received packets
	Serial.println("Transmit NTP Request");
	sendNTPpacket(timeServer);
	uint32_t beginWait = millis();
	while (millis() - beginWait < 1500) {
		int size = Udp.parsePacket();
		if (size >= NTP_PACKET_SIZE) {
			Serial.println("NTP Response");
			Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
			unsigned long secsSince1900;
			// convert four bytes starting at location 40 to a long integer
			secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
			secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
			secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
			secsSince1900 |= (unsigned long)packetBuffer[43];
			return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
		}
	}
	Serial.println("No NTP Response :-(");
	return 0; // return 0 if unable to get the time
}

// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address){
	// set all bytes in the buffer to 0
	memset(packetBuffer, 0, NTP_PACKET_SIZE);
	// Initialize values needed to form NTP request
	// (see URL above for details on the packets)
	packetBuffer[0] = 0b11100011;   // LI, Version, Mode
	packetBuffer[1] = 0;     // Stratum, or type of clock
	packetBuffer[2] = 6;     // Polling Interval
	packetBuffer[3] = 0xEC;  // Peer Clock Precision
	// 8 bytes of zero for Root Delay & Root Dispersion
	packetBuffer[12]  = 49;
	packetBuffer[13]  = 0x4E;
	packetBuffer[14]  = 49;
	packetBuffer[15]  = 52;
	// all NTP fields have been given values now
	// you can send a packet requesting a timestamp:                 
	Udp.beginPacket(address, 123); //NTP requests are to port 123
	Udp.write(packetBuffer, NTP_PACKET_SIZE);
	Udp.endPacket();
}

You can't use D10-D13 for the SoftwareSerial pins. Those are used for the SPI. That is how the ethernet shield communicates with the Arduino. Since you are using SoftwareSerial, I presume an Uno, so try D8 and D9 for the rx/tx pins instead.

I feel dumb; I've actually opened a thread concerning SPI a while back. If I had bothered to remember that thread, I would have selected another pin from the start. Some orange coloured Sugru on the SPI pins should help me remember from now on.

On the plus side: it works now!

Thanks! :)