Help with Arduino based APC UPS Status Monitor Sketch [Solved]

Hello,

I am fairly new to the Arduino environment. I have programmed in C/C++/C# for years so the language makes sense but I'm struggling with the order of processing.

I would like to create an inexpensive Arduino + Ethernet based RS232 Monitor for the APC 3000 Smart UPS. I have successfully logged into the RS232 port of the UPS from a computer and mapped out all needed serial commands and responses...
I have an RS232 to TTL board for the arduino. Serial Communication seems fine.
My Ethernet Shield is a Speed Studio v1.1

My trouble is this:
I need to request the page from a web browser (Intermapper Network monitoring software). The Arduino, once the request is received, then sends four commands over serial to the UPS, Waiting for a response in between each command. Once all responses are captured, the information will be sent as a JSON encoded string to the web browser (Monitoring Software).

What do I need to accomplish this?...
I have tried several Sketches and pieced several together.
So Far I can seperately:
A) get a webpage to flush to the Client
B) get data from serial after a command is sent

When I get a web request, the the sketch issues the first command successfully and captures the response, but does not flush the data to the web browser. It is like while(Serial.available() >0) kills the EthernetClient when it completes.

Thanks for any guidance you can provide,
Jameson

Thanks for any guidance you can provide,

Thank you for posting the troublesome code.

This is one of the cobbled together sketches.
This one is only working on the basis of 1 of the 4 Status commands passed and the response captured. Also their is no JSON encoding happening

/*
  Web Server
 
 A simple web server that shows the value of the analog input pins.
 using an Arduino Wiznet Ethernet shield. 
 
 Circuit:
 * Ethernet shield attached to pins 10, 11, 12, 13
 * Analog inputs attached to pins A0 through A5 (optional)
 
 created 18 Dec 2009
 by David A. Mellis
 modified 9 Apr 2012
 by Tom Igoe
 
 */

#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip( 192,168,100,79 );
IPAddress gateway( 192,168,100,3 );
IPAddress subnet( 255,255,255,0 );
IPAddress ns( 192,168,100,21 );
int LoopCnt = 0;
int dataCollected = 0;
int HeadersRead = 0;
String data="";
/*
StartSmart Mode (Send Y, Recieve "SM")
BatteryLevel (f, Percentage)
Load (P, Receive Decimal)
SelfTestResult (X, "OK" - good battery, "BT" - failed due to insufficient capacity, "NG" - failed due to overload, "NO" - no results available (no test performed in last 5 minutes)
EstimatedRunTime(j, (Int in Minutes terminated by :)
Status (Q, Should be "08")

*/

// Initialize the Ethernet server library
// with the IP address and port you want to use 
// (port 80 is default for HTTP):
EthernetServer server(80);

void setup() {
 // Open serial communications and wait for port to open:
  Serial.begin(2400);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip, ns, gateway, subnet); 
  server.begin();
}

void printPage(String content,EthernetClient client )
{
  	  // send a standard http response header
  	  client.println("HTTP/1.1 200 OK");
  	  client.println("Content-Type: text/html");
  	  client.println("Connnection: close");
  	  client.println();
  	  client.println("<!DOCTYPE HTML>");
  	  client.println("<html>");
  	  client.println(content);
  	  // output the value of each analog input pin
  	  client.println("</html>");
}

void loop() {
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    if (client.connected()) {
      if (client.available() ) {
        char c = client.read();  
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
           
          if(LoopCnt == 0)
          {
			//Start Smart Protocl on UPS
            //Serial.println("Y");
			
			//Get Unit Status (Looking for "08" as a return Code
            Serial.println("Q");
            LoopCnt = 1;
          }
          HeadersRead = 1;
         
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } 
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }

		if(HeadersRead == 1 && dataCollected == 1)
		{
			Serial.println("dumping page to browser");
			printPage(data,client);		

			dataCollected = 0;
			LoopCnt = 0;
			data = "";
			HeadersRead = 0;
			// close the connection:
			delay(1);
			client.stop();
		}
	}
  }
	while (Serial.available() > 0) {
	char rt = Serial.read();
	if(rt == '\n' || rt == '\r')
	{
		//if the line ending is \r\n or \n\r or there is trailing data.. flush it out of the buffer
		Serial.flush();
		dataCollected = 1;
	}
	else
	{
		data.concat(rt);
	}
		Serial.print(rt);
	}     
}

For one thing, Serial.flush() does not do what your comment implies.

I need to request the page from a web browser

You can't. A web browser is a client. You can only make requests to servers.

I'm not sure that I completely understand what you are trying to do. If the Arduino is acting as a server, then you can send it a request to make it do stuff, and provide a response. Presumably that response is the JSON string you are referring to.

Serial data transmission is asynchronous. You send some data. Sooner or later, maybe, some data comes back. It may, or may not be the complete response. You seem to be expecting to hang up from the client making the request, then see if there is serial data. That won't work.

You'll need to implement your own mechanism for waiting for the reply when you send the Q. Then, send the next command, and wait for the response. Repeat until all the commands have been sent, and all the replies received. Then, formulate a response to the client.

Paul,

Thank you for the reply. I do not mean to misuse or confuse client and server. The Intermapper Network Monitor is the Client. It will request UPS Status information from the arduino (Server).

You have described exactly what I want to do, Just more concise.

My struggle is, I cannot figure out how to set the serial events in motion once the page request comes in to the arudino and then once the serial events have completed (Based on captures and checks) return it to the client.

The sketch above allows me to start the process, send the first command, capture the response, but it does not pass that response back to the client. If I stop the client's page request from the client and then resend it. the Data flushes.

I think i have possibly mis-constructed the serial listener?

For one thing, Serial.flush() does not do what your comment implies.

I must have found old info on Serial.flush(). After your comment I looked it up.

Waits for the transmission of outgoing serial data to complete. (Prior to Arduino 1.0, this instead removed any buffered incoming serial data.

Do you know if there is a function in Arduino 1.0 that is the equivalent of the old "flush()"?

Paul,

Thank you for your help. I believe with your suggestions I was able to figure this out.
If you have time, Give my code a quick look below for any errors or arduino short comings.

/*
  Web Server
 
 A simple web server that shows the value of the analog input pins.
 using an Arduino Wiznet Ethernet shield. 
 
 Circuit:
 * Ethernet shield attached to pins 10, 11, 12, 13
 * Analog inputs attached to pins A0 through A5 (optional)
 
 created 18 Dec 2009
 by David A. Mellis
 modified 9 Apr 2012
 by Tom Igoe
 
 */

#include <SPI.h>
#include <Ethernet.h>
#include <Time.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip( 192,168,100,79 );
IPAddress gateway( 192,168,100,3 );
IPAddress subnet( 255,255,255,0 );
IPAddress ns( 192,168,100,21 );
unsigned long lastRequestTime = -11;
int dataCollected = 0;
int HeadersRead = 0;
int sendBreak = 0;
String data="";
String rcvData = "";
char termCmd[] = { 'Y','Q','P','j' };
#define TIMEOUT 15
#define NUM_CMDS (sizeof(termCmd)/sizeof(char)) //array size
/*
StartSmart Mode (Send Y, Recieve "SM")
BatteryLevel (f, Percentage)
Load (P, Receive Decimal)
SelfTestResult (X, "OK" - good battery, "BT" - failed due to insufficient capacity, "NG" - failed due to overload, "NO" - no results available (no test performed in last 5 minutes)
EstimatedRunTime(j, (Int in Minutes terminated by :)
Status (Q, Should be "08")

*/

// Initialize the Ethernet server library
// with the IP address and port you want to use 
// (port 80 is default for HTTP):
EthernetServer server(80);

void setup() {
	Serial.begin(2400);
	// start the Ethernet connection and the server:
	Ethernet.begin(mac, ip, ns, gateway, subnet); 
	server.begin();
}

void printPage(String content,EthernetClient client )
{
  	  // send a standard http response header
  	  client.println("HTTP/1.1 200 OK");
  	  client.println("Content-Type: text/html");
  	  client.println("Connnection: close");
  	  client.println();
  	  client.println("<!DOCTYPE HTML>");
  	  client.println("<html>");
  	  client.println(content);
  	  client.println("</html>");
}

void printError(int ErrorClass, EthernetClient client)
{
	String content = "";
	switch (ErrorClass) {
		case 1: // an error occured
			content = "An Error has occured";
			break;
		case 2:    // time out
			content = "Request Timed Out";
			break;
		case 3: // Reloaded within 15 seconds - Resend last data set 
			content = data;
			break;
		default:
			content = "You should not be seeing this";
	}
	
	printPage(content,client);
}

void gatherUPSData()
{
	int startTime = now();
	String lastAction = "";
	for( int j = 0; j <  NUM_CMDS; j++)
	{
		char cmd = termCmd[j];
		int rcvCmplt = 0;
		rcvData = "";
		while(sendBreak == 0 && (now() - startTime) < TIMEOUT && rcvCmplt == 0)
		{
			if(lastAction == "send")
			{  
				while(Serial.available() > 0 && dataCollected == 0)
				{
					char rt = Serial.read();
					if(rt == '\n' || rt == '\r')
					{
						lastAction = "receive";
						if(cmd != 'Y')
						{
  							data.concat("\"");
							data.concat(rcvData);
							data.concat("\"");
						}
						if((NUM_CMDS - 1) == j)
						{
							dataCollected = 1;
							data.concat("}");
						}
						else if(cmd != 'Y')
						{
							data.concat(",");
						}
						rcvCmplt = 1;
						clearSerialReadBuffer();
					}
					else
					{
						if(cmd != 'Y')
						{
							rcvData.concat(rt);
						}
					}
				}
				while(Serial.available() > 0 && dataCollected != 0)
				{
					clearSerialReadBuffer();
				}
			}
			else if((lastAction == "receive" || lastAction == "") && dataCollected == 0)
			{
				clearSerialReadBuffer();
				//send next command
				Serial.write(cmd);
				lastAction = "send";
				if(j == 0)
				{
					data.concat("{");
				}
				if(cmd != 'Y')
				{
					data.concat("\"");
					data.concat(cmd);
					data.concat("\":");
				}
				startTime = now();
			}
			else
			{
				clearSerialReadBuffer();
				sendBreak = 1;
			}
		}
		if(sendBreak > 0)
		{
			break;
		}
	}

	if((now() - startTime) >= TIMEOUT)
	{
		// Timed Out
		sendBreak = 2;
	}
}

void clearSerialReadBuffer()
{
	while(Serial.available() > 0)
	{
		Serial.read();
	}
}

void loop() {
	// listen for incoming clients
	EthernetClient client = server.available();
	if (client && (now() - lastRequestTime) <= TIMEOUT){
		// Resend Last Request
		printError(3,client);
                delay(1);
		client.stop();
	}
	else if (client) {
		// New Client
		data = "";
		// an http request ends with a blank line
		boolean currentLineIsBlank = true;
		while (client.connected()) {
			if (client.available() ) {
				char c = client.read();  
				// if you've gotten to the end of the line (received a newline
				// character) and the line is blank, the http request has ended,
				// so you can send a reply
				if (c == '\n' && currentLineIsBlank) {
					HeadersRead = 1;
					// Gathering Data.. Waiting for Serial Responses
					while (HeadersRead == 1 && dataCollected == 0)
					{
						gatherUPSData();
						if(sendBreak > 0)
						{
							printError(sendBreak,client); 
							sendBreak = 0;
							break; 
						}
					}
					if(HeadersRead == 1 && dataCollected == 1)
					{
						//Sending Page Data to Client
						printPage(data,client);		
						dataCollected = 0;
						HeadersRead = 0;
					}
					break;
				}
				if (c == '\n') {
				// you're starting a new line
				currentLineIsBlank = true;
				} 
				else if (c != '\r') {
				// you've gotten a character on the current line
				currentLineIsBlank = false;
				}
			}
		}
		// give the web browser time to receive the data
		delay(1);
		// close the connection:
		client.stop();
		// Forced Client Disconnect
		lastRequestTime = now();
	}
}

Sorry to dig up an old post but I too am interested in using an Arduino to manage an APC ups with Serial only.

Did you have any success in this area? I have the arduino hooked up with the max232 but have not been able to get a reply from the UPS.

Cheers.