Naos v1.x: Difference between revisions
No edit summary |
|||
Line 4: | Line 4: | ||
= Release = | = Release = | ||
* Last update: Apr. | * Last update: Apr. 08, 2010 | ||
* Tested | * Tested using IDE: Arduino Alpha v0018 x86 | ||
* Naos Version: 1.1.4 | * Naos Version: 1.1.4.2 | ||
= Notes = | = Notes = | ||
'''Apr. | '''Apr. 08, 2010''': Released v1.1.4.2 | ||
== WARNING == | == WARNING == | ||
Line 38: | Line 38: | ||
= Code = | = Code = | ||
* [http://nodeassassin.org/files/naos/naos_1.1.4. | * [http://nodeassassin.org/files/naos/naos_1.1.4.2.c Download] v1.1.4.2 source. | ||
<source lang="c"> | <source lang="c"> | ||
Line 45: | Line 45: | ||
#include <stdint.h> // Library for standard integer types (guarantees the size of an int). | #include <stdint.h> // Library for standard integer types (guarantees the size of an int). | ||
#include <stdlib.h> // Library for things like 'sizeof()' and 'itoa()'. | #include <stdlib.h> // Library for things like 'sizeof()' and 'itoa()'. | ||
// IMPORTANT NOTE! | |||
// Be sure to update the IP Address, Subnet Mask, Default Gateway, MAC | |||
// address and serial number before loading this software into your Node | |||
// Assassin! | |||
/* | /* | ||
Line 50: | Line 55: | ||
- Digimer | - Digimer | ||
Version: 1.1.4. | Version: 1.1.4.2 | ||
- Release: Apr. | - Release: Apr. 08, 2010 | ||
License: | License: | ||
Line 59: | Line 64: | ||
- Hacklab.TO: The idea for this device was born there. | - Hacklab.TO: The idea for this device was born there. | ||
- Christopher Olah; Came up with the name "Node Assassin". | - Christopher Olah; Came up with the name "Node Assassin". | ||
- Mark Loit: Taught me enough C to write version 1.0 of | - Mark Loit: Taught me enough C to write version 1.0 of NAOS! | ||
Bugs: | Bugs: | ||
Line 68: | Line 73: | ||
- To query the state of the nodes, send: | - To query the state of the nodes, send: | ||
- 00:0 | - 00:0 | ||
- To query the Node Assassin's details, send: | |||
- 00:1 | |||
- The integer after the '00:' is reserved for future queries. | - The integer after the '00:' is reserved for future queries. | ||
- To set the state of a node, send: | - To set the state of a node, send: | ||
- XX:Y | - XX:Y | ||
- XX is the zero-padded node ID number; 01, 02, 03 | - XX is the zero-padded node ID number; 01, 02, 03 or 04 | ||
- Y is the state to set | - Y is the state to set | ||
- 0 releases the fence and lets the node boot. | - 0 releases the fence and lets the node boot. | ||
Line 84: | Line 91: | ||
- 01:1 | - 01:1 | ||
- To release the fence and thus let the node boot, send: | - To release the fence and thus let the node boot, send: | ||
- 01: | - 01:0 | ||
- Sending any other non-standard command will generate an error message and no | - Sending any other non-standard command will generate an error message and no | ||
action will be taken. | action will be taken. | ||
Line 99: | Line 106: | ||
Changes: | Changes: | ||
- v1.1.4.2 | |||
- Updated/Cleaned up the comments in the code. | |||
- Changed the prefix for bad commands from 'ERR' to 'ERROR'. | |||
- v1.1.4.1 | - v1.1.4.1 | ||
- Fixed the numbering of Nodes in '00:0' status requests. | - Fixed the numbering of Nodes in '00:0' status requests. | ||
Line 131: | Line 141: | ||
// This will mark it as a locally administered MAC address. For example, | // This will mark it as a locally administered MAC address. For example, | ||
// use "{ 0x02, 0x00, 0x00, 0xFF, 0xF0, 0xAA }". | // use "{ 0x02, 0x00, 0x00, 0xFF, 0xF0, 0xAA }". | ||
byte mac[] = { 0x00, | byte mac[] = { 0x02, 0x00, 0x00, 0xFF, 0xF0, 0xAA }; | ||
// Arduino IP, netmask and gateway. | // Arduino IP, netmask and gateway. | ||
byte ip[] = { 192, 168, 1, 66 }; | byte ip[] = { 192, 168, 1, 66 }; | ||
// Netmask defaults to 255.255.255.0. | // Netmask defaults to 255.255.255.0. | ||
byte nm[] = { 255, 255, 255, 0 }; | byte nm[] = { 255, 255, 255, 0 }; | ||
// Default gateway defaults to IP with the last octal set to 1. | // Default gateway defaults to IP with the last octal set to 1. | ||
byte dg[] = { 192, 168, 1, 1 }; | byte dg[] = { 192, 168, 1, 1 }; | ||
// The user-set name of the node, up to sixteen characters long. | // The user-set name of the node, up to sixteen characters long. | ||
Line 154: | Line 159: | ||
// one by AN!. Otherwise, use 'PR####' where the digit section is your | // one by AN!. Otherwise, use 'PR####' where the digit section is your | ||
// internal tracking number. | // internal tracking number. | ||
char serialNumber[7]=" | char serialNumber[7]="PR0001"; | ||
char osVersion[7]="v1.1.4. | char osVersion[7]="v1.1.4.2"; | ||
char buildDate[11]="2010-04-03"; | char buildDate[11]="2010-04-03"; | ||
Line 211: | Line 216: | ||
{ | { | ||
// Variables | // Variables | ||
uint8_t node=0; // | uint8_t node=0; // Node to work on, if applicable. | ||
uint8_t state=0; // The state | uint8_t state=0; // The state to set the node(s) to. | ||
int node_power_pin=0; // This will contain the actual pin mapped to | int node_power_pin=0; // This will contain the actual pin mapped to | ||
// the requested node's power pin (digital pin). | // the requested node's power pin (digital pin). | ||
Line 223: | Line 228: | ||
uint8_t nf_state; // This will contain the feed state. | uint8_t nf_state; // This will contain the feed state. | ||
char nodeASCII[3]; // ASCII representation of node number. This is | char nodeASCII[3]; // ASCII representation of node number. This is | ||
// '3' because of 'first char' + 'second char' + terminating <NUL> | // '3' because of 'first char' + 'second char' | ||
// + terminating <NUL> | |||
char command[5]; // 5 chars "XX:Y" + <NUL> | char command[5]; // 5 chars "XX:Y" + <NUL> | ||
int index = 0; // | int index = 0; // An index to increment and reset in loops. | ||
char macString[6]; // MAC address. | char macString[6]; // MAC address. | ||
Line 239: | Line 245: | ||
if (('\n' == command[index]) || ('\r' == command[index]) ) | if (('\n' == command[index]) || ('\r' == command[index]) ) | ||
{ | { | ||
break; // EOL found, break out of the | break; // EOL found, break out of the loop. | ||
} | } | ||
index++; // advance the index. | index++; // advance the index. | ||
} | } | ||
// on a valid line the above | // on a valid line the above loop will exit with index == 4 | ||
// If there is no message, nothing to do but exit. | // If there is no message, nothing to do but exit. | ||
Line 256: | Line 262: | ||
if (4 > index) | if (4 > index) | ||
{ | { | ||
printMessage("ERROR: Message too short.\n"); | printMessage("ERROR: Message too short.\n"); | ||
printMessage("EOM\n"); | printMessage("EOM\n"); | ||
Line 266: | Line 271: | ||
{ | { | ||
char ch; | char ch; | ||
printMessage("ERROR: Message too long.\n"); | printMessage("ERROR: Message too long.\n"); | ||
printMessage("EOM\n"); | printMessage("EOM\n"); | ||
Line 274: | Line 278: | ||
if( ('\n' == ch) || ('\r' == ch) ) | if( ('\n' == ch) || ('\r' == ch) ) | ||
{ | { | ||
break; // break out of the | break; // break out of the loop. | ||
} | } | ||
} | } | ||
Line 303: | Line 307: | ||
// Do the math to turn the ASCII node number into a binary | // Do the math to turn the ASCII node number into a binary | ||
// value. | // value. | ||
node=command[0]-'0'; // First digit convertion (ie: '1' (0x31)-'0' (0x30) = 0x01 = "0000 0001 (dec. 1)"). | node=command[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*=10; // Shift to the first base-10 position. | ||
node+=command[1]-'0'; // Now 'node' contains the binary version of the ASCII two-digit value read off of telnet. | node+=command[1]-'0'; // Now 'node' contains the binary | ||
// version of the ASCII two-digit value | |||
// read off of telnet. | |||
// Convert the "node" value to the actual power and reset pins. | // Convert the "node" value to the actual power and reset pins. | ||
// Because each node consumes | // Because each node consumes two digital output pins, I need | ||
// double the 'node' value and then add the digital out | // to double the 'node' value and then add the digital out | ||
// This will give me the reset pin directly, and then | // offset. This will give me the reset pin directly, and then | ||
// by one to get the power button pin. The Feed pin is | // subtract by one to get the power button pin. The Feed pin is | ||
// the node's value plus the input offset. | // simple the node's value plus the input offset less one. | ||
node_power_pin=(((node*2)-1)+(FIRSTOUTPIN-1)); | node_power_pin=(((node*2)-1)+(FIRSTOUTPIN-1)); | ||
node_reset_pin=((node*2)+(FIRSTOUTPIN-1)); | node_reset_pin=((node*2)+(FIRSTOUTPIN-1)); | ||
Line 318: | Line 326: | ||
// Do the math to turn the state number into a binary value. | // Do the math to turn the state number into a binary value. | ||
state=command[3]-'0'; // Now 'state' contains the binary version. | state=command[3]-'0'; // Now 'state' contains the binary | ||
// version. | |||
// copy the ASCII node name for the response messages | // copy the ASCII node name for the response messages so that | ||
// we don't have to convert it back later. | |||
nodeASCII[0] = command[0]; | nodeASCII[0] = command[0]; | ||
nodeASCII[1] = command[1]; | nodeASCII[1] = command[1]; | ||
nodeASCII[2] = 0; // <NUL> terminate it | nodeASCII[2] = 0; // <NUL> terminate it | ||
// | // Make sure the requested node is actually available on this | ||
// Node Assassin. | |||
if (node > NODECOUNT) | if (node > NODECOUNT) | ||
{ | { | ||
// | // Too high a value! | ||
// Make my NODECOUNT an ASCII value so that I can print | |||
// it by reversing the convertion to binary done | |||
// earlier. The following two lines will be converted | |||
// by the compiler, so there is no run-time penalty for | |||
// the math here | |||
// | |||
// Move from the 'tens' posiition into the '1' position | |||
// and add '0' to get the ASCII value. | |||
nodeASCII[0]=(NODECOUNT/10)+'0'; | |||
// The modulous returns my real "one" position. | |||
nodeASCII[1]=(NODECOUNT%10)+'0'; | |||
// nodeASCII was <NUL> terminated earlier at 3, so no | |||
// need to do it again here | |||
printMessage("ERROR: Max node value: ["); printMessage(nodeASCII); printMessage("]\n"); | printMessage("ERROR: Max node value: ["); printMessage(nodeASCII); printMessage("]\n"); | ||
printMessage("EOM\n"); | printMessage("EOM\n"); | ||
Line 345: | Line 362: | ||
if (state > 3) | if (state > 3) | ||
{ | { | ||
// Node state can't be higher than ' | // Node state can't be higher than '3' on this model. | ||
printMessage("ERROR: Invalid state.\n"); | printMessage("ERROR: Invalid state.\n"); | ||
printMessage("EOM\n"); | printMessage("EOM\n"); | ||
Line 352: | Line 368: | ||
} | } | ||
// Check | // Check if this is an info request. | ||
if (0 == node) | if (0 == node) | ||
{ | { | ||
// If the message request is '0', return states. | // If the message request is '0', return states. | ||
// If the message request is '1', return NA info. | // If the message request is '1', return NA info. | ||
if (0 == state) | if (0 == state) | ||
{ | { | ||
Line 363: | Line 378: | ||
printMessage("Node states: \n"); | printMessage("Node states: \n"); | ||
// Make my NODECOUNT an ASCII value so that I can print it by reversing the convertion to binary done earlier. | // Make my NODECOUNT an ASCII value so that I | ||
// can print it by reversing the convertion to | |||
nodeASCII[0]=(NODECOUNT/10)+'0'; | // binary done earlier. | ||
nodeASCII[1]=(NODECOUNT%10)+'0'; | nodeASCII[0]=(NODECOUNT/10)+'0'; | ||
nodeASCII[1]=(NODECOUNT%10)+'0'; | |||
printMessage("- Node Count: "); printMessage(nodeASCII); printMessage("\n"); | printMessage("- Node Count: "); printMessage(nodeASCII); printMessage("\n"); | ||
/* | /* | ||
Future optimization: | NOTE: - Future optimization: | ||
The division and modulus in the loop can be | The division and modulus in the loop can be | ||
processing wise, as the compiler cannot do the | expensive processing wise, as the compiler | ||
cannot do the calculation at compile time. As | |||
we are simply itteratively looping and | |||
incrementing, we can increment the ASCII value | |||
directly, removing the need for any division or | |||
modulus operations. | |||
*/ | */ | ||
// Loop through the supported number of nodes | |||
// and return their fence and feed states. | |||
for (int node=1; node<=NODECOUNT; node++) | for (int node=1; node<=NODECOUNT; node++) | ||
{ | { | ||
// ' | // 'node' is the current, one-based | ||
// node number. | |||
nodeASCII[0]=(node/10)+'0'; | nodeASCII[0]=(node/10)+'0'; | ||
// The modulous returns my real one position. | // The modulous returns my real one | ||
// position. | |||
nodeASCII[1]=(node%10)+'0'; | nodeASCII[1]=(node%10)+'0'; | ||
Line 393: | Line 413: | ||
node_feed_pin=(node+(FIRSTINPIN-1)); | node_feed_pin=(node+(FIRSTINPIN-1)); | ||
// Read in the current states. | |||
np_state = digitalRead(node_power_pin); | np_state = digitalRead(node_power_pin); | ||
nr_state = digitalRead(node_reset_pin); | nr_state = digitalRead(node_reset_pin); | ||
nf_state = digitalRead(node_feed_pin); | nf_state = digitalRead(node_feed_pin); | ||
// And finally report the states. | |||
printMessage("- Node "); printMessage(nodeASCII); printMessage(": "); | printMessage("- Node "); printMessage(nodeASCII); printMessage(": "); | ||
printMessage("P"); printMessage((LOW == np_state) ? "0, " : "1, "); | printMessage("P"); printMessage((LOW == np_state) ? "0, " : "1, "); | ||
printMessage("R"); printMessage((LOW == nr_state) ? "0, " : "1, "); | printMessage("R"); printMessage((LOW == nr_state) ? "0, " : "1, "); | ||
printMessage("F"); printMessage((LOW == nf_state) ? "0\n" : "1\n"); | printMessage("F"); printMessage((LOW == nf_state) ? "0\n" : "1\n"); | ||
// This is extra debug info for the | |||
// serial console. | |||
Serial.print(" P.Pin: "); Serial.println(node_power_pin); | Serial.print(" P.Pin: "); Serial.println(node_power_pin); | ||
Serial.print(" R.Pin: "); Serial.println(node_reset_pin); | Serial.print(" R.Pin: "); Serial.println(node_reset_pin); | ||
Line 415: | Line 441: | ||
// seperators (:) and the | // seperators (:) and the | ||
// NULL terminator | // NULL terminator | ||
char ipASCII[16]; | // IP Address, netmask and default gateway in | ||
char nmASCII[16]; | // dotted decimal formats. | ||
char dgASCII[16]; | char ipASCII[16]; | ||
char nmASCII[16]; | |||
char dgASCII[16]; | |||
int j=0; | int j=0; | ||
// Generate the MAC address ASCII string. | |||
for(int i=0; i < (sizeof(mac) / sizeof(mac[0])); i++) | for(int i=0; i < (sizeof(mac) / sizeof(mac[0])); i++) | ||
{ | { | ||
// If I have a value, that is, is '1' | // If I have a value, that is, is '1' | ||
// or higher, start by inserting a | // or higher, start by inserting a | ||
// | // colon (:) to seperate the octets. | ||
// This method avoids a preceeding ' | // This method avoids a preceeding ':'. | ||
if (i) | if (i) | ||
{ | { | ||
macASCII[j++] = ':'; | macASCII[j++] = ':'; | ||
} | } | ||
// We're | // We're zero-padding single hex | ||
// so if this value is less | // values, so if this value is less | ||
// (0000 1111), add a leading '0'. | // than 16 (0000 1111), add a leading | ||
// '0'. | |||
if (mac[i]<16) | if (mac[i]<16) | ||
{ | { | ||
Line 444: | Line 475: | ||
} | } | ||
// IP, netmask and default gateway. | // Now we will generate the IP address, netmask | ||
// and default gateway ASCII strings. | |||
j=0; | j=0; | ||
for (int i=0; i < (sizeof(ip) / sizeof(ip[0])); i++) | for (int i=0; i < (sizeof(ip) / sizeof(ip[0])); i++) | ||
Line 451: | Line 483: | ||
// or higher, start by inserting a | // or higher, start by inserting a | ||
// period (.) to seperate the octets. | // period (.) to seperate the octets. | ||
if (i) | if (i) | ||
{ | { | ||
ipASCII[j++] = '.'; | ipASCII[j++] = '.'; | ||
} | } | ||
// Convert the integer to an ASCII. | // Convert the integer to an ASCII. | ||
itoa(ip[i], &ipASCII[j++], 10); | itoa(ip[i], &ipASCII[j++], 10); | ||
// Increment 'j' one or two places, | // Increment 'j' one or two places, | ||
// depending on the value of 'j'. | // depending on the value of 'j'. | ||
Line 466: | Line 499: | ||
for (int i=0; i < (sizeof(nm) / sizeof(nm[0])); i++) | for (int i=0; i < (sizeof(nm) / sizeof(nm[0])); i++) | ||
{ | { | ||
if (i) | if (i) | ||
{ | { | ||
nmASCII[j++] = '.'; | nmASCII[j++] = '.'; | ||
} | } | ||
itoa(nm[i], &nmASCII[j++], 10); | itoa(nm[i], &nmASCII[j++], 10); | ||
if(nm[i]>9) j++; | if(nm[i]>9) j++; | ||
if(nm[i]>99) j++; | if(nm[i]>99) j++; | ||
Line 484: | Line 510: | ||
for (int i=0; i < (sizeof(ip) / sizeof(ip[0])); i++) | for (int i=0; i < (sizeof(ip) / sizeof(ip[0])); i++) | ||
{ | { | ||
if (i) | if (i) | ||
{ | { | ||
dgASCII[j++] = '.'; | dgASCII[j++] = '.'; | ||
} | } | ||
itoa(dg[i], &dgASCII[j++], 10); | itoa(dg[i], &dgASCII[j++], 10); | ||
if(dg[i]>9) j++; | if(dg[i]>9) j++; | ||
if(dg[i]>99) j++; | if(dg[i]>99) j++; | ||
} | } | ||
// Make my NODECOUNT an ASCII value so that I can print it by reversing the convertion to binary done earlier. | |||
// Make my NODECOUNT an ASCII value so that I | |||
nodeASCII[0]=(NODECOUNT/10)+'0'; | // can print it by reversing the convertion to | ||
nodeASCII[1]=(NODECOUNT%10)+'0'; | // binary done earlier. | ||
nodeASCII[0]=(NODECOUNT/10)+'0'; | |||
nodeASCII[1]=(NODECOUNT%10)+'0'; | |||
// Print the info. | // Print the info. | ||
Line 519: | Line 540: | ||
{ | { | ||
// Unrecognized message request. | // Unrecognized message request. | ||
printMessage("ERROR: Unknown message request.\n"); | printMessage("ERROR: Unknown message request.\n"); | ||
printMessage("EOM\n"); | printMessage("EOM\n"); | ||
Line 526: | Line 546: | ||
return; | return; | ||
} | } | ||
// If I am still alive, I am going to set a node to a given | |||
// state. | |||
// Make the node number printable. | // Make the node number printable. | ||
nodeASCII[0]=((node+1)/10)+'0'; | nodeASCII[0]=((node+1)/10)+'0'; | ||
nodeASCII[1]=((node+1)%10)+'0'; | nodeASCII[1]=((node+1)%10)+'0'; | ||
// Subtract 1 from node to make it zero-based. | // Subtract 1 from node to make it zero-based. | ||
node--; | node--; | ||
// | |||
// Which state I received will determine what I actually do to | |||
// the power and reset output pins. | |||
if (0 == state) | if (0 == state) | ||
{ | { | ||
/ | // Tell the user what is going to happen. | ||
printMessage("Releasing "); printMessage(nodeASCII); printMessage("\n"); | printMessage("Releasing "); printMessage(nodeASCII); printMessage("\n"); | ||
// Get prior states. | // Get prior states to make my return string a little | ||
// more useful. | |||
np_state = digitalRead(node_power_pin); | np_state = digitalRead(node_power_pin); | ||
nr_state = digitalRead(node_reset_pin); | nr_state = digitalRead(node_reset_pin); | ||
// Release power. | // Release power. | ||
digitalWrite(node_power_pin, LOW); | digitalWrite(node_power_pin, LOW); | ||
printMessage(" - Power "); printMessage((LOW == np_state) ? "wasn't fenced.\n" : "released.\n"); | printMessage(" - Power "); printMessage((LOW == np_state) ? "wasn't fenced.\n" : "released.\n"); | ||
// Release reset. | // Release reset. | ||
digitalWrite(node_reset_pin, LOW); | digitalWrite(node_reset_pin, LOW); | ||
printMessage(" - Reset "); printMessage((LOW == nr_state) ? "wasn't fenced.\n" : "released.\n"); | printMessage(" - Reset "); printMessage((LOW == nr_state) ? "wasn't fenced.\n" : "released.\n"); | ||
// Wait one second and then check that the pin states are actually LOW. | |||
// Wait one second and then check that the pin states | |||
// are actually LOW. I could probably get away with a | |||
// smaller delay, but this works. | |||
delay(1000); | delay(1000); | ||
// Check the current/new states | |||
np_state = digitalRead(node_power_pin); | np_state = digitalRead(node_power_pin); | ||
nr_state = digitalRead(node_reset_pin); | nr_state = digitalRead(node_reset_pin); | ||
// Report an error if either are still high. Not sure | |||
// how this might happen, but hey, let's be thorough. | |||
printMessage(" - Status: "); printMessage(((HIGH == np_state) || (HIGH == nr_state)) ? "ERROR! Fence failed to release.\n" : "SUCCESS!\n"); | printMessage(" - Status: "); printMessage(((HIGH == np_state) || (HIGH == nr_state)) ? "ERROR! Fence failed to release.\n" : "SUCCESS!\n"); | ||
printMessage("EOM\n"); | printMessage("EOM\n"); | ||
Line 556: | Line 593: | ||
else if (1 == state) | else if (1 == state) | ||
{ | { | ||
/ | // Fence the node! This is perhaps the most critical | ||
// operation to get right. Start by telling the user | |||
// what we're going to do. | |||
printMessage("Fencing node "); printMessage(nodeASCII); printMessage(":\n"); | printMessage("Fencing node "); printMessage(nodeASCII); printMessage(":\n"); | ||
Line 564: | Line 603: | ||
// If either power or reset where HIGH already, release | // If either power or reset where HIGH already, release | ||
// them and wait one second. This will | // them and wait one second. This will allow us to | ||
// | // re-run the fence attempt were an earlier one may | ||
// have failed. | |||
if (HIGH == np_state) | if (HIGH == np_state) | ||
{ | { | ||
printMessage(" - Power was fenced, releasing."); | printMessage(" - Power was fenced, releasing."); | ||
digitalWrite(node_power_pin, LOW); | digitalWrite(node_power_pin, LOW); | ||
Line 574: | Line 613: | ||
if (HIGH == nr_state) | if (HIGH == nr_state) | ||
{ | { | ||
printMessage(" - Reset was fenced, releasing."); | printMessage(" - Reset was fenced, releasing."); | ||
digitalWrite(node_reset_pin, LOW); | digitalWrite(node_reset_pin, LOW); | ||
} | } | ||
// If either were HIGH, sleep for one second. | // If either were HIGH, sleep for one second. | ||
if ((HIGH == np_state) || (HIGH == nr_state)) | if ((HIGH == np_state) || (HIGH == nr_state)) | ||
Line 584: | Line 623: | ||
} | } | ||
// Fence the reset for one second. | // Fence the reset for one second to immediately | ||
// disable the node. | |||
digitalWrite(node_reset_pin, HIGH); | digitalWrite(node_reset_pin, HIGH); | ||
printMessage(" - Reset fenced.\n"); | printMessage(" - Reset fenced.\n"); | ||
delay(1000); | delay(1000); | ||
// Release reset. | // Release reset so that we can hit the power next. | ||
// This is required because some machines will not | |||
// power down if the reset switch is closed. | |||
digitalWrite(node_reset_pin, LOW); | digitalWrite(node_reset_pin, LOW); | ||
printMessage(" - Reset released.\n"); | printMessage(" - Reset released.\n"); | ||
delay(1000); | delay(1000); | ||
// | // Now fence the power button to begin the forced power | ||
// down. Then wait five seconds and check the power | |||
// feed state. | |||
digitalWrite(node_power_pin, HIGH); | digitalWrite(node_power_pin, HIGH); | ||
printMessage(" - Power fenced.\n"); | printMessage(" - Power fenced.\n"); | ||
delay(5000); | delay(5000); | ||
int fence_ok=1; | int fence_ok=1; | ||
nf_state = digitalRead(node_feed_pin); | nf_state = digitalRead(node_feed_pin); | ||
// If the feed is still HIGH, wait another 25 seconds | |||
// and check a second time. | |||
if (HIGH == nf_state) | if (HIGH == nf_state) | ||
{ | { | ||
printMessage(" - WARNING: Node still on, waiting.\n"); | printMessage(" - WARNING: Node still on, waiting.\n"); | ||
delay(25000); | delay(25000); | ||
nf_state = digitalRead(node_feed_pin); | nf_state = digitalRead(node_feed_pin); | ||
// If it is still HIGH, error out. | |||
if (HIGH == nf_state) | if (HIGH == nf_state) | ||
{ | { | ||
printMessage(" - ERROR! Node still on. FENCE FAILED!\n"); | printMessage(" - ERROR! Node still on. FENCE FAILED!\n"); | ||
fence_ok=0; | fence_ok=0; | ||
Line 622: | Line 664: | ||
if (1 == fence_ok) | if (1 == fence_ok) | ||
{ | { | ||
printMessage(" - SUCCESS!\n"); | printMessage(" - SUCCESS!\n"); | ||
// Re-fence the reset switch. | |||
// Re-fence the reset switch to disable the | |||
// node's front-panel switches. | |||
digitalWrite(node_reset_pin, HIGH); | digitalWrite(node_reset_pin, HIGH); | ||
printMessage(" - Node's front-panel switches locked.\n"); | printMessage(" - Node's front-panel switches locked.\n"); | ||
} | } | ||
Line 633: | Line 675: | ||
else if (2 == state) | else if (2 == state) | ||
{ | { | ||
// Hit the power switch for one second. | // Hit the power switch for one second. | ||
// current state so that I can properly | // First check the current state so that I can properly | ||
// happening. | // report what is happening. | ||
nf_state = digitalRead(node_feed_pin); | nf_state = digitalRead(node_feed_pin); | ||
if (HIGH == nf_state) | if (HIGH == nf_state) | ||
{ | { | ||
// | // The node is on, so report that we are | ||
// | // shutting down. | ||
printMessage("Initiating ACPI power down\n"); | printMessage("Initiating ACPI power down\n"); | ||
} | } | ||
else | else | ||
{ | { | ||
// | // The node was off, so report that we are | ||
// | // booting. | ||
printMessage("Booting node\n"); | printMessage("Booting node\n"); | ||
} | } | ||
// Get prior states. If either are HIGH, Error out and | // Get prior states. If either are HIGH, Error out and | ||
// do nothing. | // do nothing. The node needs to be released from the | ||
// fence before state 2 can be applied to it. | |||
np_state = digitalRead(node_power_pin); | np_state = digitalRead(node_power_pin); | ||
nr_state = digitalRead(node_reset_pin); | nr_state = digitalRead(node_reset_pin); | ||
if ((HIGH == np_state) || (HIGH == nr_state)) | if ((HIGH == np_state) || (HIGH == nr_state)) | ||
{ | { | ||
printMessage(" - ERROR! Node is fenced. Release and try again.\n"); | |||
} | } | ||
else | else | ||
{ | { | ||
// We're good to proceed. | |||
digitalWrite(node_power_pin, HIGH); | digitalWrite(node_power_pin, HIGH); | ||
printMessage(" - Power button closed.\n"); | printMessage(" - Power button closed.\n"); | ||
delay(1000); | delay(1000); | ||
Line 682: | Line 724: | ||
{ | { | ||
// Node is off, so no need to proceed. | // Node is off, so no need to proceed. | ||
printMessage(" - WARNING! Node is already off.\n"); | printMessage(" - WARNING! Node is already off.\n"); | ||
} | } | ||
Line 689: | Line 730: | ||
// Fence power | // Fence power | ||
digitalWrite(node_power_pin, HIGH); | digitalWrite(node_power_pin, HIGH); | ||
printMessage(" - Forcing node off.\n"); | printMessage(" - Forcing node off.\n"); | ||
delay(5000); | delay(5000); | ||
// Check that the power is off and wait another 25 seconds if it isn't. | // Check that the power is off and wait another | ||
// 25 seconds if it isn't. | |||
int fence_ok=1; | int fence_ok=1; | ||
nf_state = digitalRead(node_feed_pin); | nf_state = digitalRead(node_feed_pin); | ||
if (HIGH == nf_state) | if (HIGH == nf_state) | ||
{ | { | ||
printMessage(" - WARNING! Node still on, waiting.\n"); | printMessage(" - WARNING! Node still on, waiting.\n"); | ||
delay(25000); | delay(25000); | ||
Line 704: | Line 744: | ||
if (HIGH == nf_state) | if (HIGH == nf_state) | ||
{ | { | ||
printMessage(" - ERROR! Node still on. FORCED SHUTDOWN FAILED!\n"); | printMessage(" - ERROR! Node still on. FORCED SHUTDOWN FAILED!\n"); | ||
fence_ok=0; | fence_ok=0; | ||
Line 713: | Line 752: | ||
if (1 == fence_ok) | if (1 == fence_ok) | ||
{ | { | ||
printMessage(" - SUCCESS!\n"); | printMessage(" - SUCCESS!\n"); | ||
digitalWrite(node_power_pin, LOW); | digitalWrite(node_power_pin, LOW); | ||
printMessage(" - Fence released.\n"); | printMessage(" - Fence released.\n"); | ||
} | } | ||
Line 728: | Line 765: | ||
void printError(const char *message) | void printError(const char *message) | ||
{ | { | ||
// Print the message to the serial bus and the client. | // Print the message to the serial bus and the client. I know this is | ||
// | // dirty but it represents the one line string. | ||
printMessage(" | printMessage("ERROR: Bad command: [" ); printMessage(message); printMessage("]\n" ); | ||
printMessage("EOM\n"); | printMessage("EOM\n"); | ||
} | } |
Latest revision as of 15:54, 8 April 2010
Node Assassin :: Naos v1.x |
Release
- Last update: Apr. 08, 2010
- Tested using IDE: Arduino Alpha v0018 x86
- Naos Version: 1.1.4.2
Notes
Apr. 08, 2010: Released v1.1.4.2
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. He also gets the credit for the project's tag line.
Code
- Download v1.1.4.2 source.
#include <Ethernet.h> // Arduino's ethernet library.
#include <ctype.h> // Library for testing and character manipulation.
#include <stdint.h> // Library for standard integer types (guarantees the size of an int).
#include <stdlib.h> // Library for things like 'sizeof()' and 'itoa()'.
// IMPORTANT NOTE!
// Be sure to update the IP Address, Subnet Mask, Default Gateway, MAC
// address and serial number before loading this software into your Node
// Assassin!
/*
Author:
- Digimer
Version: 1.1.4.2
- Release: Apr. 08, 2010
License:
- The GNU GPL v2.0
Thanks:
- Hacklab.TO: The idea for this device was born there.
- Christopher Olah; Came up with the name "Node Assassin".
- Mark Loit: Taught me enough C to write version 1.0 of NAOS!
Bugs:
- None known at this time.
Protocol:
- Telnet (or similar) to the IP and Port set below.
- To query the state of the nodes, send:
- 00:0
- To query the Node Assassin's details, send:
- 00:1
- The integer after the '00:' is reserved for future queries.
- To set the state of a node, send:
- XX:Y
- XX is the zero-padded node ID number; 01, 02, 03 or 04
- Y is the state to set
- 0 releases the fence and lets the node boot.
- 1 fences the requested node.
- 2 Fence for one second. Useful for rebooting a node or for when a port
is connected to a node's power button to boot or gracefully power down
a node (via ACPI).
- 3 Fence for five seconds. Only useful when connected to a power button.
This allows the Node Assassin to force a frozen server to power off.
- Example:
- To fence Node 01, send:
- 01:1
- To release the fence and thus let the node boot, send:
- 01:0
- Sending any other non-standard command will generate an error message and no
action will be taken.
Note:
- This device implements NO security. You MUST install in on a private, secure
intranet or similar back channel. Installing it on the same LAN as the
storage devices is advised.
- Changing this file will have no effect until the program is recompiled and
uploaded to the Node Assassin.
To Do:
- Make naming the device and setting it's network settings configurable.
Changes:
- v1.1.4.2
- Updated/Cleaned up the comments in the code.
- Changed the prefix for bad commands from 'ERR' to 'ERROR'.
- v1.1.4.1
- Fixed the numbering of Nodes in '00:0' status requests.
- v1.1.4
- Changed the version number to correspond to the matching supported Node
Assassin hardware version.
- Added the concept of "nodes" which are groups of two digital outputs plus
one analog input treated as a digital input.
- Changed the states. Most critically, 0 now releases the fence and 1 fences
the node. Further, "fencing" is no longer simply closing the switch. A
fence triggers a sequence of switch open and closing to accomplish the
fence.
- Added support for node feed detection and added errors and warnings to
state calls that are based on the feed values.
- v1.0.4
- Set all output to send a final 'EOM' (End Of Message) on a new line after
all output for the fence agent to know when data has finished returning.
- Prefixed all error messages with 'ERR: '.
- v1.0.3
- Added the '00:1' query message which returns the Node Assassin's details
and identification.
- Added the '##:2' and '##:3' options.
- v1.0.2
- First release.
*/
// MAC Address; Array of six bytes.
// If you've been assigned a MAC address by AN!, or if you have your own block
// of MAC addresses, enter it here.
// NOTE! If you do not have a MAC address, choose one that starts with h02.
// This will mark it as a locally administered MAC address. For example,
// use "{ 0x02, 0x00, 0x00, 0xFF, 0xF0, 0xAA }".
byte mac[] = { 0x02, 0x00, 0x00, 0xFF, 0xF0, 0xAA };
// 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 };
// The user-set name of the node, up to sixteen characters long.
char nodeName[16]="Motoko";
// The serial number.
// NOTE! Only set a serial number starting with 'NA####' if you were assigned
// one by AN!. Otherwise, use 'PR####' where the digit section is your
// internal tracking number.
char serialNumber[7]="PR0001";
char osVersion[7]="v1.1.4.2";
char buildDate[11]="2010-04-03";
// This is the port that I will listen on.
#define PORT 238
// Setup the server.
Server server = Server(PORT);
// Setup my digital out pins.
// CONSTRAINT: Input and Output pins must be ssigned sequentially
#define NODECOUNT 4
// The digital pins are output
#define FIRSTOUTPIN 2
// The analog pins are treated as digital inputs, so I count them from 14 - 19.
#define FIRSTINPIN 14
// My function prototypes.
void printError(const char *message);
void printMessage(const char *message);
// Setup the Arduino on boot.
void setup()
{
// Setup the IP info.
Ethernet.begin(mac, ip, dg, nm);
// Print the serial port welcom message.
Serial.begin(9600);
Serial.print("Node Assassin: ["); Serial.print(nodeName); Serial.println("] starting.");
// Iterator to setup the digital pins to output and to set them
// initially to LOW.
for (int pin = FIRSTOUTPIN; pin < (FIRSTOUTPIN+(NODECOUNT*2)); pin++)
{
Serial.print("Set - pin: ["); Serial.print(pin); Serial.println("] to OUTPUT/LOW.");
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
// Iterator to setup the analog pins as digital inputs.
for (int pin = FIRSTINPIN; pin < (FIRSTINPIN+NODECOUNT); pin++)
{
Serial.print("Set - pin: ["); Serial.print(pin); Serial.println("] to INPUT.");
pinMode(pin, INPUT);
}
Serial.println("Ready!");
// Start the server listening for connections.
server.begin();
}
// And GO!
void loop()
{
// Variables
uint8_t node=0; // Node to work on, if applicable.
uint8_t state=0; // The state to set the node(s) to.
int node_power_pin=0; // This will contain the actual pin mapped to
// the requested node's power pin (digital pin).
uint8_t np_state; // This will contain the power state.
int node_reset_pin=0; // This will contain the actual pin mapped to
// the requested node's reset pin (digital pin).
uint8_t nr_state; // This will contain the reset state.
int node_feed_pin=0; // This will contain the actual pin mapped to
// the requested node's feed pin (analog pin).
uint8_t nf_state; // This will contain the feed state.
char nodeASCII[3]; // ASCII representation of node number. This is
// '3' because of 'first char' + 'second char'
// + terminating <NUL>
char command[5]; // 5 chars "XX:Y" + <NUL>
int index = 0; // An index to increment and reset in loops.
char macString[6]; // MAC address.
// Start the network library.
Client client=server.available();
if (client)
{
// process the input in a line-based manner, allowing for 1
// command per line
while ((-1 != (command[index] = client.read()) ) && (5 > index))
{
// exit at the end of line
if (('\n' == command[index]) || ('\r' == command[index]) )
{
break; // EOL found, break out of the loop.
}
index++; // advance the index.
}
// on a valid line the above loop will exit with index == 4
// If there is no message, nothing to do but exit.
// Coding note: By putting 0 first, I can never accidentally
// set the variable to '0' with an accidental single-equal.
if (0 == index)
{
return;
}
// sanity check on length
if (4 > index)
{
printMessage("ERROR: Message too short.\n");
printMessage("EOM\n");
return;
}
// Spool off whatever is left in the buffer/line in case it was a string longer than 4.
if (5 == index)
{
char ch;
printMessage("ERROR: Message too long.\n");
printMessage("EOM\n");
while (-1 != (ch = client.read()) )
{
// exit at the end of line
if( ('\n' == ch) || ('\r' == ch) )
{
break; // break out of the loop.
}
}
return;
}
// <NUL> terminate the string
command[index] = 0;
// Parse the string; Error if anything isn't right.
// Make sure we have a colon in the right location
if (':' != command[2])
{
// Error
printError(command);
return;
}
// Make sure the other characters are digits
if (!isdigit(command[0]) || !isdigit(command[1]) || !isdigit(command[3]))
{
// Error
printError(command);
return;
}
// No need to check for the terminator or newline at the end,
// that was taken care of in the read loop.
// Do the math to turn the ASCII node number into a binary
// value.
node=command[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+=command[1]-'0'; // Now 'node' contains the binary
// version of the ASCII two-digit value
// read off of telnet.
// Convert the "node" value to the actual power and reset pins.
// Because each node consumes two digital output pins, I need
// to double the 'node' value and then add the digital out
// offset. This will give me the reset pin directly, and then
// subtract by one to get the power button pin. The Feed pin is
// simple the node's value plus the input offset less one.
node_power_pin=(((node*2)-1)+(FIRSTOUTPIN-1));
node_reset_pin=((node*2)+(FIRSTOUTPIN-1));
node_feed_pin=(node+(FIRSTINPIN-1));
// Do the math to turn the state number into a binary value.
state=command[3]-'0'; // Now 'state' contains the binary
// version.
// copy the ASCII node name for the response messages so that
// we don't have to convert it back later.
nodeASCII[0] = command[0];
nodeASCII[1] = command[1];
nodeASCII[2] = 0; // <NUL> terminate it
// Make sure the requested node is actually available on this
// Node Assassin.
if (node > NODECOUNT)
{
// Too high a value!
// Make my NODECOUNT an ASCII value so that I can print
// it by reversing the convertion to binary done
// earlier. The following two lines will be converted
// by the compiler, so there is no run-time penalty for
// the math here
//
// Move from the 'tens' posiition into the '1' position
// and add '0' to get the ASCII value.
nodeASCII[0]=(NODECOUNT/10)+'0';
// The modulous returns my real "one" position.
nodeASCII[1]=(NODECOUNT%10)+'0';
// nodeASCII was <NUL> terminated earlier at 3, so no
// need to do it again here
printMessage("ERROR: Max node value: ["); printMessage(nodeASCII); printMessage("]\n");
printMessage("EOM\n");
return;
}
// Check that the requested state is sane.
if (state > 3)
{
// Node state can't be higher than '3' on this model.
printMessage("ERROR: Invalid state.\n");
printMessage("EOM\n");
return;
}
// Check if this is an info request.
if (0 == node)
{
// If the message request is '0', return states.
// If the message request is '1', return NA info.
if (0 == state)
{
// Send states
printMessage("Node states: \n");
// Make my NODECOUNT an ASCII value so that I
// can print it by reversing the convertion to
// binary done earlier.
nodeASCII[0]=(NODECOUNT/10)+'0';
nodeASCII[1]=(NODECOUNT%10)+'0';
printMessage("- Node Count: "); printMessage(nodeASCII); printMessage("\n");
/*
NOTE: - Future optimization:
The division and modulus in the loop can be
expensive processing wise, as the compiler
cannot do the calculation at compile time. As
we are simply itteratively looping and
incrementing, we can increment the ASCII value
directly, removing the need for any division or
modulus operations.
*/
// Loop through the supported number of nodes
// and return their fence and feed states.
for (int node=1; node<=NODECOUNT; node++)
{
// 'node' is the current, one-based
// node number.
nodeASCII[0]=(node/10)+'0';
// The modulous returns my real one
// position.
nodeASCII[1]=(node%10)+'0';
// Make this a bit more readable.
node_power_pin=(((node*2)-1)+(FIRSTOUTPIN-1));
node_reset_pin=((node*2)+(FIRSTOUTPIN-1));
node_feed_pin=(node+(FIRSTINPIN-1));
// Read in the current states.
np_state = digitalRead(node_power_pin);
nr_state = digitalRead(node_reset_pin);
nf_state = digitalRead(node_feed_pin);
// And finally report the states.
printMessage("- Node "); printMessage(nodeASCII); printMessage(": ");
printMessage("P"); printMessage((LOW == np_state) ? "0, " : "1, ");
printMessage("R"); printMessage((LOW == nr_state) ? "0, " : "1, ");
printMessage("F"); printMessage((LOW == nf_state) ? "0\n" : "1\n");
// This is extra debug info for the
// serial console.
Serial.print(" P.Pin: "); Serial.println(node_power_pin);
Serial.print(" R.Pin: "); Serial.println(node_reset_pin);
Serial.print(" F.Pin: "); Serial.println(node_feed_pin);
}
printMessage("EOM\n");
}
else if (1 == state)
{
/* Setup some strings. */
// MAC address.
printMessage("Node info: \n");
char macASCII[18]; // Enough room for 6 bytes
// of hex [12 digits], colon
// seperators (:) and the
// NULL terminator
// IP Address, netmask and default gateway in
// dotted decimal formats.
char ipASCII[16];
char nmASCII[16];
char dgASCII[16];
int j=0;
// Generate the MAC address ASCII string.
for(int i=0; i < (sizeof(mac) / sizeof(mac[0])); i++)
{
// If I have a value, that is, is '1'
// or higher, start by inserting a
// colon (:) to seperate the octets.
// This method avoids a preceeding ':'.
if (i)
{
macASCII[j++] = ':';
}
// We're zero-padding single hex
// values, so if this value is less
// than 16 (0000 1111), add a leading
// '0'.
if (mac[i]<16)
{
macASCII[j] = '0';
itoa(mac[i], &macASCII[(j)+1], 16);
}
else
{
itoa(mac[i], &macASCII[j], 16);
}
j+=2;
}
// Now we will generate the IP address, netmask
// and default gateway ASCII strings.
j=0;
for (int i=0; i < (sizeof(ip) / sizeof(ip[0])); i++)
{
// If I have a value, that is, is '1'
// or higher, start by inserting a
// period (.) to seperate the octets.
if (i)
{
ipASCII[j++] = '.';
}
// Convert the integer to an ASCII.
itoa(ip[i], &ipASCII[j++], 10);
// Increment 'j' one or two places,
// depending on the value of 'j'.
if(ip[i]>9) j++;
if(ip[i]>99) j++;
}
j=0;
for (int i=0; i < (sizeof(nm) / sizeof(nm[0])); i++)
{
if (i)
{
nmASCII[j++] = '.';
}
itoa(nm[i], &nmASCII[j++], 10);
if(nm[i]>9) j++;
if(nm[i]>99) j++;
}
j=0;
for (int i=0; i < (sizeof(ip) / sizeof(ip[0])); i++)
{
if (i)
{
dgASCII[j++] = '.';
}
itoa(dg[i], &dgASCII[j++], 10);
if(dg[i]>9) j++;
if(dg[i]>99) j++;
}
// Make my NODECOUNT an ASCII value so that I
// can print it by reversing the convertion to
// binary done earlier.
nodeASCII[0]=(NODECOUNT/10)+'0';
nodeASCII[1]=(NODECOUNT%10)+'0';
// Print the info.
printMessage("- Node Name: ..... "); printMessage(nodeName); printMessage("\n");
printMessage("- Port Count: .... "); printMessage(nodeASCII); printMessage("\n");
printMessage("- NAOS Version: .. "); printMessage(osVersion); printMessage("\n");
printMessage("- Serial Number: . "); printMessage(serialNumber); printMessage("\n");
printMessage("- Build Date: .... "); printMessage(buildDate); printMessage("\n");
printMessage("- MAC address: ... "); printMessage(macASCII); printMessage("\n");
printMessage("- IP address: .... "); printMessage(ipASCII); printMessage("\n");
printMessage("- Subnet Mask: ... "); printMessage(nmASCII); printMessage("\n");
printMessage("- Default Gateway: "); printMessage(dgASCII); printMessage("\n");
printMessage("EOM\n");
}
else
{
// Unrecognized message request.
printMessage("ERROR: Unknown message request.\n");
printMessage("EOM\n");
}
return;
}
// If I am still alive, I am going to set a node to a given
// state.
// Make the node number printable.
nodeASCII[0]=((node+1)/10)+'0';
nodeASCII[1]=((node+1)%10)+'0';
// Subtract 1 from node to make it zero-based.
node--;
// Which state I received will determine what I actually do to
// the power and reset output pins.
if (0 == state)
{
// Tell the user what is going to happen.
printMessage("Releasing "); printMessage(nodeASCII); printMessage("\n");
// Get prior states to make my return string a little
// more useful.
np_state = digitalRead(node_power_pin);
nr_state = digitalRead(node_reset_pin);
// Release power.
digitalWrite(node_power_pin, LOW);
printMessage(" - Power "); printMessage((LOW == np_state) ? "wasn't fenced.\n" : "released.\n");
// Release reset.
digitalWrite(node_reset_pin, LOW);
printMessage(" - Reset "); printMessage((LOW == nr_state) ? "wasn't fenced.\n" : "released.\n");
// Wait one second and then check that the pin states
// are actually LOW. I could probably get away with a
// smaller delay, but this works.
delay(1000);
// Check the current/new states
np_state = digitalRead(node_power_pin);
nr_state = digitalRead(node_reset_pin);
// Report an error if either are still high. Not sure
// how this might happen, but hey, let's be thorough.
printMessage(" - Status: "); printMessage(((HIGH == np_state) || (HIGH == nr_state)) ? "ERROR! Fence failed to release.\n" : "SUCCESS!\n");
printMessage("EOM\n");
}
else if (1 == state)
{
// Fence the node! This is perhaps the most critical
// operation to get right. Start by telling the user
// what we're going to do.
printMessage("Fencing node "); printMessage(nodeASCII); printMessage(":\n");
// Get prior states.
np_state = digitalRead(node_power_pin);
nr_state = digitalRead(node_reset_pin);
// If either power or reset where HIGH already, release
// them and wait one second. This will allow us to
// re-run the fence attempt were an earlier one may
// have failed.
if (HIGH == np_state)
{
printMessage(" - Power was fenced, releasing.");
digitalWrite(node_power_pin, LOW);
}
if (HIGH == nr_state)
{
printMessage(" - Reset was fenced, releasing.");
digitalWrite(node_reset_pin, LOW);
}
// If either were HIGH, sleep for one second.
if ((HIGH == np_state) || (HIGH == nr_state))
{
delay(1000);
}
// Fence the reset for one second to immediately
// disable the node.
digitalWrite(node_reset_pin, HIGH);
printMessage(" - Reset fenced.\n");
delay(1000);
// Release reset so that we can hit the power next.
// This is required because some machines will not
// power down if the reset switch is closed.
digitalWrite(node_reset_pin, LOW);
printMessage(" - Reset released.\n");
delay(1000);
// Now fence the power button to begin the forced power
// down. Then wait five seconds and check the power
// feed state.
digitalWrite(node_power_pin, HIGH);
printMessage(" - Power fenced.\n");
delay(5000);
int fence_ok=1;
nf_state = digitalRead(node_feed_pin);
// If the feed is still HIGH, wait another 25 seconds
// and check a second time.
if (HIGH == nf_state)
{
printMessage(" - WARNING: Node still on, waiting.\n");
delay(25000);
nf_state = digitalRead(node_feed_pin);
// If it is still HIGH, error out.
if (HIGH == nf_state)
{
printMessage(" - ERROR! Node still on. FENCE FAILED!\n");
fence_ok=0;
}
}
// Proceed if fence succeeded.
if (1 == fence_ok)
{
printMessage(" - SUCCESS!\n");
// Re-fence the reset switch to disable the
// node's front-panel switches.
digitalWrite(node_reset_pin, HIGH);
printMessage(" - Node's front-panel switches locked.\n");
}
printMessage("EOM\n");
}
else if (2 == state)
{
// Hit the power switch for one second.
// First check the current state so that I can properly
// report what is happening.
nf_state = digitalRead(node_feed_pin);
if (HIGH == nf_state)
{
// The node is on, so report that we are
// shutting down.
printMessage("Initiating ACPI power down\n");
}
else
{
// The node was off, so report that we are
// booting.
printMessage("Booting node\n");
}
// Get prior states. If either are HIGH, Error out and
// do nothing. The node needs to be released from the
// fence before state 2 can be applied to it.
np_state = digitalRead(node_power_pin);
nr_state = digitalRead(node_reset_pin);
if ((HIGH == np_state) || (HIGH == nr_state))
{
printMessage(" - ERROR! Node is fenced. Release and try again.\n");
}
else
{
// We're good to proceed.
digitalWrite(node_power_pin, HIGH);
printMessage(" - Power button closed.\n");
delay(1000);
digitalWrite(node_power_pin, LOW);
printMessage(" - Power button opened.\n");
}
printMessage("EOM\n");
}
else if (3 == state)
{
// Hit the power switch for five seconds. Make sure the
// power feed is low, and if not, wait another 25
// seconds and check again.
printMessage("Forcing power down of node "); printMessage(nodeASCII); printMessage(":\n");
// If the node is off, then there is nothing to do.
nf_state = digitalRead(node_feed_pin);
if (LOW == nf_state)
{
// Node is off, so no need to proceed.
printMessage(" - WARNING! Node is already off.\n");
}
else
{
// Fence power
digitalWrite(node_power_pin, HIGH);
printMessage(" - Forcing node off.\n");
delay(5000);
// Check that the power is off and wait another
// 25 seconds if it isn't.
int fence_ok=1;
nf_state = digitalRead(node_feed_pin);
if (HIGH == nf_state)
{
printMessage(" - WARNING! Node still on, waiting.\n");
delay(25000);
nf_state = digitalRead(node_feed_pin);
if (HIGH == nf_state)
{
printMessage(" - ERROR! Node still on. FORCED SHUTDOWN FAILED!\n");
fence_ok=0;
}
}
// Proceed if fence succeeded.
if (1 == fence_ok)
{
printMessage(" - SUCCESS!\n");
digitalWrite(node_power_pin, LOW);
printMessage(" - Fence released.\n");
}
}
printMessage("EOM\n");
}
}
}
// The error handling function.
void printError(const char *message)
{
// Print the message to the serial bus and the client. I know this is
// dirty but it represents the one line string.
printMessage("ERROR: Bad command: [" ); printMessage(message); printMessage("]\n" );
printMessage("EOM\n");
}
void printMessage(const char *message)
{
// Print the message to the serial bus and the client.
Serial.print(message);
server.write(message);
}
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. |