/* Functional sensor/actuator skeleton program. * * This contains enough primitives to actually do something, * including local parameters. * * Contains a small command string interpreter for commands * from the host computer: * * { nnnnn } C * * Where * * { nnnn } is an optional decimal value * * C is a command (function) character * * Examples: * * 1s turns LED on pin 13 ON * 0s turns LED on pin 13 OFF * 4d set number of A to Ds to report on * 280r set AtoD report rate to every 280mS * * Tom Jennings 11 Nov 2006 */ /* PIN DEFINITIONS (sic) */ int ledPin = 13; // pin with LED attached int swPin= 11; // simple switch sensor input int ADpin= 0; // AtoD pins are always 0 - 5 int DApin= 9; // our D to A output pin /* Global parameters. */ int bitRate= 9600; // serial port speed unsigned ADMAX= 0; // how many A to Ds to report (0 - 5 max) unsigned ADrate= 100; // how often we report AD value /* Little state machines used by the code. */ int swState= -1; // its state is unknown int ADstate= 0; // AtoD state machine long ADtime; // last time we read the A to Ds /* Crap used to handle input from the host computer. */ char c= 0; // character we input unsigned N= 0; // where we build input values void setup() {} /* There's no real need to use Arduino's "processing"-like program structure. It's just baby code. */ void loop () { // Initialization crap in the usual place. pinMode(ledPin,OUTPUT); // declare the LED's pin as output pinMode(swPin, INPUT); // make switch pin an input, Serial.begin(bitRate); // setup serial port Serial.print("OK\n"); // dispel rumors of our demise ADtime= millis(); // the time is now! /* Loop here forever doing whatever it is we are doing. Note that none of the called routines loop internally, wait for an external event, or anything else that blocks execution. */ while (1) { c= getc(); // get character from host, inpN(c); // (process numbers), sensor(); // check on the sensor, actuator(c); // maybe do something, AtoD(); // read some pots etc. } } /* Get a character from the serial port if one is available. Return the character else 0. */ char getc() { if (Serial.available() < 1) { // if no chars from the host, return (0); // return nothing. } c= Serial.read(); // read from the serial port, return c; // return it. } /**************************************************************** A to D STATE MACHINE: The ATMEGA8 chip contains six A to D channels. When it is past time to report on an AtoD converter (ADrate) read from the current AtoD (ADstate), output it, then advance to the next AtoD (ADMAX). Note the three externally controlled parameters. ****************************************************************/ void AtoD () { int v; if (millis() - ADtime < ADrate) { // is it time yet? return; // nope. } ADtime= millis(); // do this now, not later. v= analogRead(ADstate); // read the data, Serial.print ("AD"); Serial.print (ADstate, DEC); Serial.print ("="); Serial.println (v, DEC); if (++ADstate > ADMAX) { ADstate= 0; } // select A/D for next time. } /**************************************************************** SENSOR STATE-MACHINE: Watch an input pin, and tell the host when the pin changes state. Note that this does not "block", eg. it doesn't loop; it tests, does something or not, then falls through. ****************************************************************/ void sensor () { int n; n= digitalRead(swPin); // sample the sensor, if (n != swState) { // if it changed state, swState= n; // remember new state Serial.print("S="); Serial.println(n); } } /**************************************************************** ACTUATOR STATE-MACHINE: When we receive a character from the host, see if it's a command we should execute. Note that this does not "block", eg. it doesn't loop; it tests, does something or not, then falls through or returns. ****************************************************************/ void actuator (char c) { switch (c) { /* Actions. */ case 's': digitalWrite (ledPin, N); N= 0; break; case 'o': analogWrite (9, N); N= 0; break; /* Parameter setting. */ case 'd': if (N <= 5) { ADMAX= N; N= 0; } break; case 'r': if ((N >= 10) && (N <= 50000)) { ADrate= N; N= 0; } break; /* These are cosmetic; but it allows this controller to accept "line oriented" output. */ case 13: Serial.print("\n"); break; case 10: break; /* This case is TOTALLY USELESS -- it's here only to remind you what happens when a digit character is eaten by inpN(). Nothing. Not clear? Trace the code step by step. */ case 0: break; } } /* Input a variable-precision number, statelessly, given a character from the serial port. If the character is a digit incorporate it into the number and return 0. If it is not a digit, the character is returned unmodified. The magic constant 48 is not magic if you know what ASCII is! */ char inpN (char c) { char n; n= c - 48; // see if ASCII 0 - 9 if (n < 0) { return c; } // nope if (n > 9) { return c; } // nope N= N * 10 + n; // shift in new digit return 0; // eat the character. }