Naos v1.x

From Alteeve Wiki
Revision as of 03:04, 24 January 2010 by Digimer (talk | contribs) (moved Naos v1.0 to Naos v1.x: Make it minor-version agnostic)
Jump to navigation Jump to search

 Node Assassin :: Naos v1.x


Release

  • Last update: Jan. 23, 2010
  • Tested Against: Arduino Alpha v0017
  • Naos Version: 1.0

Notes

WARNING

USE THIS CODE AT YOUR OWN RISK!

It's the default warning for everything related to this project, but it's worth repeating here. This code come with no guarantee in any way, shape or form. This has only been tested on my board, the Arduino Duemilanove with the ATmega320 chip connected to the Wiznet W5100 ethernet shield. I can't guarantee it will work on any other hardware, but I would love to hear from you if you do try it elsewhere!

Changes To Make BEFORE Uploading

Before you upload this, be sure to set an IP address and MAC address that suits your network.

  • Change the network address to fit your network. The defaults are:
    • IP Addr: 192.168.1.66
    • Netmask: 255.255.255.0
    • Gateway: 192.168.1.1
  • In the sketch below, alter the byte mac[] value to a value unique on you network. To be honest, the default is probably ok.

Use

Copy this code into the 'Arduino alpha' loader, test it and then upload it to your board.

Thanks

The majority of this first version was made possible thanks the generous patience of Mark Loit at hacklab.to who spent the day giving me a crash course in C and helping me diagnose my first circuit.

Code

#include <Ethernet.h>	// Arduino's enthernet library.
#include <ctype.h>	// Library for testing and character manipulation.
#include <stdint.h>	// Library for standard integer types (guarantees the size of an int).

/*
Author:
 - Digimer
Version: 1.0.1
 - Release: 2010-01-23
Thanks:
 - Hacklab.TO
 - Christopher Olah; Name "Node Assassin"
 - Mark Loit:        Taught me enough C to write ver. 1.0 of NaOS!
Bugs:
 - None known at this time.
*/

// 00:X is a request for info where 'X' is the requested data. Currently '0' is only data type and returns the current state of all nodes.
// [01-99]:[0-1] Sends a command to set the state of node [01-99] to on [:1] or off [:0].

// MAC Address; Array of six bytes.
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF };
// Arduino IP, netmask and gateway.
byte ip[] = { 192, 168, 1, 66 };
// Netmask defaults to 255.255.255.0.
byte nm[] = { 255, 255, 255, 0 };
// Default gateway defaults to IP with the last octal set to 1.
byte dg[] = { 192, 168, 1, 1 };

// This is the port that I will listen on.
#define port 238

// Setup the server.
Server server = Server(port);

// My function prototypes.
void printError(char *reading);

// Setup my digital out pins.
#define maxnode 5
#define firstnode 2

// Setup
void setup()
{
	// Setup the IP info.
	Ethernet.begin(mac, ip, dg, nm);
	
	// Print what comes in over telnet.
	Serial.begin(9600);
	Serial.println("Node Assassin: 'Ariel' now listening for orders.");
	
	// Iterator
	for (int i=firstnode; i<(firstnode+maxnode); i++)
	{
		// Set my pin 'i' to be output.
		pinMode(i, OUTPUT);
		// and set it initially to be low.
		digitalWrite(i, LOW);
	}
	
	// Start the server listening for connections.
	server.begin();
}

// And GO!
void loop()
{
	// Variables
	uint8_t node=0;		// The device I am working on.
	uint8_t state=0;	// The (new?) state of the pin.
	
	// Start the network library.
	Client client=server.available();
	if (client)
	{
		char reading[7];	// 4 chars (XX:Y) + '\n' or '\r\n' or <NUL>' + double-increment below
		int i=0;
		while (((reading[i]=client.read())!=-1)&&(i<5))
		{
			i++;
		}
		// Spool off whatever is left in the buffer in case it was a string longer than 5.
		if (client.available())
		{
			Serial.println("Message too long. Format is 'XX:Y' where 'XX' is the xero-padded node number.");
			server.write("Message too long. Format is 'XX:Y' where 'XX' is the xero-padded node number.\n");
			while (-1 != client.read());
			return;
		}
		// Double increment to make sure my test works even when I don't get an '\n' or '\r\n'.
		reading[i++]=0;
		reading[i]=0;
		
		// Parse the string.
		// Format: XX:Y where XX is the node to work on and Y is the new state
		// Make sure the string is formatted properly and print an error if not.
		if (reading[2] != ':')
		{
			// Error
			printError(reading);
			return;
		}
		if (!isdigit(reading[0]) || !isdigit(reading[1]) || !isdigit(reading[3]))
		{
			// Error
			printError(reading);
			return;
		}
		// Next two check the termination
		if ((reading[4] != 0) && (reading[4] != '\n') && (reading[4] != '\r'))
		{
			// Extraneous terminated string.
			printError(reading);
			return;
		}
		if ((reading[5] != 0) && (reading[5] != '\n') && (reading[5] != '\r'))
		{
			// Extraneous terminated string.
			printError(reading);
			return;
		}
		
		// Do the math to turn the node number into a binary value.
		node=reading[0]-'0';	// First digit convertion (ie: '1' (0x31)-'0' (0x30) = 0x01 = "0000 0001 (dec. 1)").
		node*=10;		// Shift to the first base-10 position.
		node+=reading[1]-'0';	// Now 'node' contains the binary version of the ASCII two-digit value read off of telnet.
		
		// Do the math to turn the state number into a binary value.
		state=reading[3]-'0';	// Now 'state' contains the binary version.
		
		// Check the node.
		if (node > 5)
		{
			// Node number can't be higher than '05' on this model.
			Serial.println("This fence only supports up to '05' nodes.");
			server.write("This fence only supports up to '05' nodes.\n");
			return;
		}
		if (state > 1)
		{
			// Node number can't be higher than '05' on this model.
			Serial.println("Invalid state received. Send 'XX:0' to kill a node, XX:1 to release a node");
			server.write("Invalid state received. Send 'XX:0' to kill a node, XX:1 to release a node\n");
			return;
		}
		
		// Check is this is an info request.
		// By putting 0 first, I can never accidentally set 'node' to '0' with an accidental single-equal.
		if (0 == node)
		{
			// Send states
			Serial.println("Node states: ");
			server.write("Node states: \n");
			
			// Make my maxnode an ASCII value so that I can print it by reversing the convertion to binary done earlier.
			char saynode[3];		// This is '3' because of 'first char' + 'second char' + terminating '0'.
			saynode[0]=(maxnode/10)+'0';	// Move from the 'tens' posiition into the '1' position and add '0' to get the ASCII value.
			saynode[1]=(maxnode%10)+'0';	// The modulous returns my real one position.
			saynode[2]=0;			// Null terminated.
			
			Serial.print("- Max Node: "); Serial.println(saynode);
			server.write("- Max Node: "); server.write(saynode); server.write("\n");
			for (int i=0; i<maxnode; i++)
			{
				// 'i' is the current, printable node number.
				saynode[0]=((i+1)/10)+'0';	// The '+1' makes the node 1-based instead of 0-based.
				saynode[1]=((i+1)%10)+'0';	// The modulous returns my real one position.
				
				char saystate = digitalRead(i+firstnode);	// i + pin offset.
				Serial.print("- Node "); Serial.print(saynode); Serial.print(": "); Serial.println((LOW == saystate) ? "Running" : "Fenced!" );
				server.write("- Node "); server.write(saynode); server.write(": "); server.write((LOW == saystate) ? "Running\n" : "Fenced!\n");
			}
			Serial.println("End Message.");
			server.write("End Message.\n");
			
			return;
		}
		
		// Subtract 1 from node to make it zero-based.
		node--;
		// Set the pin based on whether 'state' is '0' or not.
		digitalWrite(node+firstnode, (0 == state) ? HIGH : LOW);
		Serial.println((1 == state) ? "Now running." : "Now Fenced!");
		server.write((1 == state) ? "Now running.\n" : "Now Fenced!\n");
	}
}

void printError(char *reading)
{
	// Copy 'reading' so that we don't lose our pointer to it in memory.
	char *source = reading;
	// Loop through the string replacing '\n' or '\r' with a <NUL>.
	while (*source)
	{
		// If this character is a newline, replace it.
		if (('\n' == *source) || ('\r' == *source))
		{
			// Found it. Set it to 0 which will kill the newline.
			*source=0;
			// Exit out of the closest encompasing while or for loop.
			break;
		}
		// Increment the source pointer for the next go-round.
		source++;
	}
	
	// Print the message to the serial bus and the client.
	// I know this is dirty but it represents the one line string.
	Serial.print("Bad command: [" ); Serial.print(reading); Serial.println("]" );
	server.write("Bad command: [" ); server.write(reading); server.write("]\n" );
}

 

Input, advice, complaints and meanderings all welcome!
Digimer digimer@alteeve.ca https://alteeve.ca/w legal stuff:  
All info is provided "As-Is". Do not use anything here unless you are willing and able to take resposibility for your own actions. © 1997-2013
Naming credits go to Christopher Olah!
In memory of Kettle, Tonia, Josh, Leah and Harvey. In special memory of Hannah, Jack and Riley.