Cooper Baker · Nerve Center Code
This arduino code was written to control a sculpture installed in a 911 call center. It receives serial ascii data through a max232 chip in order to read the gradually incrementing number of calls per shift. This number is evaluated based on a statistical analysis of shift maximums and 5 stepper motors are pulsed to discrete positions, representing the current shift's calls. The stepper motors spin dichroic glass color wheels, and are affixed within fiber-optic illuminator boxes where they change the color of light reflecting within fiber-optic cables throughout the sculpture.

nerve center.cpp
nerve center.cpp
   1:/*
   2: * Nerve Center statistics and stepper control
   3: * for the arduino environment and atmega168
   4: *
   5: * Cooper Baker 2007 . www.cooperbaker.com
   6: *
   7: * [tab] = 4
   8: *
   9: */
  10: 
  11:// [1;1H9-1-1 CALLS ANSWERED THIS SHIFT [K [2;1H1024 [K
  12: 
  13:#include <WProgram.h>               // needed by arduino environment
  14:
  15:#define BAUD            9600        // baud rate
  16:#define AVG             120         // number of shifts for averaging
  17:#define STEPS           349         // number of steps per revolution - 1
  18:#define STEP_COEFF      0.000462963 // step coefficient ( 1 / 2160 ) for demo
  19:#define STEP_DLY        25          // milliseconds per half-step (20 hz steps)
  20:#define DEMO_CALL_DLY   10000       // milliseconds between demo 'calls'
  21:
  22://- prototypes -----------------------------------------------------------------
  23:void setup( void );                 // sets up the chip
  24:void loop( void );                  // the driver loop
  25:void strip_number( char *words );   // makes an integer from the number in the ascii string
  26:void stats( void );                 // records # calls and does some math
  27:void demo_stats( void );            // makes coeff go from 0 - 1 over 6 hours
  28:void output( void );                // moves the steppers and txs debug info
  29:float scale( float value, float min, float max, float out_min, float out_max );
  30:void rx_string( void );             // gets the buffered string
  31:void cls( void );                   // clears the lcd screen
  32:void splash( void );                // fancy splash screen
  33:void reset( void );                 // reset the data and steppers
  34:
  35://- variables ------------------------------------------------------------------
  36:int     maximum[ AVG ];             // averaging AVG maximums
  37:char    string[ 128 ]   = "";       // contains the string
  38:int     report          = 0;        // number of report
  39:int     place           = 0;        // magnitude of the ascii number
  40:int     letter          = 0;        // position within the string
  41:int     num[ 10 ];                  // for ascii to int conversion
  42:int     n               = 0;        // numeral counter
  43:int     i               = 0;        // for loop counter
  44:int     calls           = 0;        // number of 911 calls
  45:int     prev_calls      = 0;        // previous calls to detect new shift
  46:float   average         = 0;        // average number of calls
  47:float   coeff           = 0;        // current call coefficient
  48:int     full            = 0;        // full data set flag
  49:int     demo            = LOW;      // demo mode flag
  50:int     update          = 1;        // update flag
  51:int     S1              = 0;        // stepper 1 step
  52:int     S2              = 0;        // stepper 2 step
  53:int     S3              = 0;        // stepper 3 step
  54:int     S4              = 0;        // stepper 4 step
  55:int     S5              = 0;        // stepper 5 step
  56:int     S1_prev         = 0;        // stepper 1 previous step
  57:int     S2_prev         = 0;        // stepper 2 previous step
  58:int     S3_prev         = 0;        // stepper 3 previous step
  59:int     S4_prev         = 0;        // stepper 4 previous step
  60:int     S5_prev         = 0;        // stepper 5 previous step
  61:
  62://- pin setup ------------------------------------------------------------------
  63:byte    demo_sw         = 2;        // demo mode pin
  64:byte    rx_ready        = 3;        // ready to receive pin
  65:byte    stepper_1       = 4;        // stepper step pins
  66:byte    stepper_2       = 7;        // . . . 
  67:byte    stepper_3       = 8;        //
  68:byte    stepper_4       = 10;       //
  69:byte    stepper_5       = 12;       //
  70:
  71://- setup ----------------------------------------------------------------------
  72:void setup( void )
  73:{
  74:    pinMode( demo_sw, INPUT );                      // set up demo mode pin
  75:    pinMode( rx_ready, OUTPUT );                    // set up rx_ready pin  
  76:    pinMode( stepper_1, OUTPUT );                   // set up stepper pins
  77:    pinMode( stepper_2, OUTPUT );                   // . . . 
  78:    pinMode( stepper_3, OUTPUT );                   // 
  79:    pinMode( stepper_4, OUTPUT );                   // 
  80:    pinMode( stepper_5, OUTPUT );                   // 
  81:    
  82:    Serial.begin( 9600 );                           // connect to serial port
  83:    cls();                                          // clear screen     
  84:    splash();                                       // splash screen
  85:    cls();                                          // clear screen
  86:    digitalWrite( rx_ready, HIGH );                 // ready for rx
  87://  demo = HIGH;
  88:}
  89:
  90://- loop -----------------------------------------------------------------------
  91:void loop( void )
  92:{                   
  93:    demo = digitalRead( demo_sw );
  94:    if( demo == HIGH )
  95:    {
  96:        demo_stats();                               // min to max over 6 hours
  97:        update = 1;                                 // make sure we output
  98:        cls();                                      // clear lcd
  99:        output();                                   // pulse the steppers
 100:        delay( DEMO_CALL_DLY );                     // delay between 'calls'
 101:    }
 102:    else
 103:    {
 104:        rx_string();                                // read rx buffer
 105:        delay( 1000 );
 106:        strip_number( &string[ 0 ] );               // get the current number of calls
 107:        stats();                                    // analyze the data set
 108:        cls();
 109:        output();                                   // pulse the steppers
 110:    }
 111:}
 112:
 113://- stats ----------------------------------------------------------------------
 114:void stats( void )
 115:{   
 116:    average = 0;                                    // init average;
 117:    
 118:    // this switch ensures that the stats are accurate if we don't have a full data set
 119:    // i.e. after boot-up or reset, before enough data has been collected to fill the set
 120:    switch( full )                                  // full data set?
 121:    {
 122:        case 0: for( i = 0 ; i < report ; i++ )     // index increment loop
 123:/* not full */      average += maximum[ i ];        // accumulate a sum
 124:                    
 125:                if( report > 0 )                    // if enough values exist to calculate average
 126:                    average = average / report;     // calculate average
 127:                else                                // otherwise
 128:                    average = calls;                // average is current calls
 129:                    
 130:                break;
 131:        
 132:        case 1: for( i = 0 ; i < AVG ; i++ )        // index increment loop
 133:/* full */          average += maximum[ i ];        // accumulate a sum
 134:
 135:                average = average / AVG;            // calculate average
 136:                break;
 137:    }
 138:    
 139:    coeff = calls / average;                        // calculate current coefficient
 140:    if( coeff > 1 )                                 // if it is larger than 1
 141:        coeff = 1;                                  // set it to 1
 142:    
 143:    // the next block of if()s calculates the individual steppers' positions
 144:    // and provides 20% overlap between stepper values
 145:    if( ( coeff >= 0 ) &&  ( coeff < 0.25 ) )       // if coeff is in stepper 1 range
 146:    {
 147:        if( S1_prev > S1 )                          // if new shift
 148:            S1_prev = 0;                            // clear previous step value
 149:        else                                        // else
 150:            S1_prev = S1;                           // remember previous step value
 151:
 152:        S1 = scale( coeff, 0.0, 0.25, 0, STEPS );   // calc stepper 1 position
 153:    }
 154:    
 155:    if( ( coeff > 0.20 ) &&  ( coeff < 0.45 ) )     // etc...
 156:    {
 157:        S1 = STEPS;
 158:        
 159:        if( S2_prev > S2 )
 160:            S2_prev = 0;
 161:        else
 162:            S2_prev = S2;
 163:        
 164:        S2 = scale( coeff, 0.20, 0.45, 0, STEPS );
 165:    }
 166:    
 167:    if( ( coeff > 0.40 ) &&  ( coeff < 0.65 ) )
 168:    {
 169:        S1 = STEPS;
 170:        S2 = STEPS;
 171:        
 172:        if( S3_prev > S3 )
 173:            S3_prev = 0;
 174:        else
 175:            S3_prev = S3;       
 176:        
 177:        S3 = scale( coeff, 0.40, 0.65, 0, STEPS );
 178:    }
 179:    if( ( coeff > 0.60 ) &&  ( coeff < 0.85 ) )
 180:    {
 181:        S1 = STEPS;
 182:        S2 = STEPS;
 183:        S3 = STEPS;
 184:        
 185:        if( S4_prev > S4 )
 186:            S4_prev = 0;
 187:        else
 188:            S4_prev = S4;
 189:        
 190:        S4 = scale( coeff, 0.60, 0.85, 0, STEPS );
 191:    }
 192:    if( ( coeff > 0.80 ) &&  ( coeff < 1 ) )
 193:    {
 194:        S1 = STEPS;
 195:        S2 = STEPS;
 196:        S3 = STEPS;
 197:        S4 = STEPS;
 198:        
 199:        if( S5_prev > S5 )
 200:            S5_prev = 0;
 201:        else
 202:            S5_prev = S5;
 203:        
 204:        S5 = scale( coeff, 0.80, 1.0, 0, STEPS );
 205:    }
 206:    if( coeff == 1 )
 207:    {
 208:        S1 = STEPS;
 209:        S2 = STEPS;
 210:        S3 = STEPS;
 211:        S4 = STEPS;
 212:        S5 = STEPS;
 213:    }
 214:}
 215:
 216://- demo -----------------------------------------------------------------------
 217:void demo_stats( void )
 218:{   
 219:    coeff = coeff + STEP_COEFF;                     // increment coefficient
 220:    if( coeff > 1 )                                 // if coeff > 1
 221:    {
 222:        coeff = 0;                                  // set coeff to 0
 223:        reset();                                    // reset steppers and data
 224:    }
 225:
 226:    // the next block of if()s calculates the individual steppers' positions
 227:    // and provides 20% overlap between stepper values
 228:    if( ( coeff >= 0 ) &&  ( coeff < 0.25 ) )       // if coeff is in stepper 1 range
 229:    {
 230:        if( S1_prev > S1 )                          // if new shift
 231:            S1_prev = 0;                            // clear previous step value
 232:        else                                        // else
 233:            S1_prev = S1;                           // remember previous step value
 234:
 235:        S1 = scale( coeff, 0.0, 0.25, 0, STEPS );   // calc stepper 1 position
 236:    }
 237:    if( ( coeff > 0.20 ) &&  ( coeff < 0.45 ) )     // etc...
 238:    {
 239:        S1 = STEPS;
 240:        
 241:        if( S2_prev > S2 )
 242:            S2_prev = 0;
 243:        else
 244:            S2_prev = S2;
 245:        
 246:        S2 = scale( coeff, 0.20, 0.45, 0, STEPS );
 247:    }
 248:    if( ( coeff > 0.40 ) &&  ( coeff < 0.65 ) )
 249:    {
 250:        S1 = STEPS;
 251:        S2 = STEPS;
 252:        
 253:        if( S3_prev > S3 )
 254:            S3_prev = 0;
 255:        else
 256:            S3_prev = S3;       
 257:        
 258:        S3 = scale( coeff, 0.40, 0.65, 0, STEPS );
 259:    }
 260:    if( ( coeff > 0.60 ) &&  ( coeff < 0.85 ) )
 261:    {
 262:        S1 = STEPS;
 263:        S2 = STEPS;
 264:        S3 = STEPS;
 265:        
 266:        if( S4_prev > S4 )
 267:            S4_prev = 0;
 268:        else
 269:            S4_prev = S4;
 270:        
 271:        S4 = scale( coeff, 0.60, 0.85, 0, STEPS );
 272:    }
 273:    if( ( coeff > 0.80 ) &&  ( coeff < 1 ) )
 274:    {
 275:        S1 = STEPS;
 276:        S2 = STEPS;
 277:        S3 = STEPS;
 278:        S4 = STEPS;
 279:        
 280:        if( S5_prev > S5 )
 281:            S5_prev = 0;
 282:        else
 283:            S5_prev = S5;
 284:        
 285:        S5 = scale( coeff, 0.80, 1.0, 0, STEPS );
 286:    }
 287:    if( coeff == 1 )
 288:    {
 289:        S1 = STEPS;
 290:        S2 = STEPS;
 291:        S3 = STEPS;
 292:        S4 = STEPS;
 293:        S5 = STEPS;
 294:    }
 295:}
 296:
 297://- strip_number ---------------------------------------------------------------
 298:void strip_number( char *words ) // need to handle serial bug...?
 299:{
 300:    letter = 0;
 301:    prev_calls = calls;
 302:    calls = 0;
 303:    place = 1;
 304:    n = 0;
 305:    
 306:    // the followig for loop reads the first number of calls encountered
 307:    // in the string, then exits
 308:    for( i = 0 ; i < 127 ; i++ )                    // read the string
 309:    {
 310:        if( ( (string[i]=='2') && (string[i+1]==';') ) )
 311:        {                                           // looking for '2;'
 312:            letter = i + 4;                         // index of start of number
 313:            while( (string[ letter ] > 47) && (string[ letter ] < 58) )
 314:            {                                       // while letter is ascii numeral
 315:                num[n] = (int)string[ letter ] - 48; // convert to int and save
 316:                letter++;                           // increment letter
 317:                n++;                                // increment num index
 318:            }
 319:            goto math;                              // do as i say, not as i do
 320:        }
 321:    }
 322:
 323:    // this chunk calculates the value of the ascii numerals
 324:    math :                                          // how did i get here?  
 325:    while( n-- )                                    // 'read' the number backwards
 326:    {           
 327:        calls += num[ n ] * place;                  // calculate # of calls
 328:        place *= 10;                                // increment magnitude
 329:    }
 330:    
 331:    // this if-else determines whether the update flag should be set
 332:    if( calls == prev_calls )                       // need to update output?
 333:        update = 0;                                 // no update if no data change
 334:    else
 335:        update = 1;                                 // update if data change
 336:
 337:    // this handles the Serial.read() bug
 338:    if( (calls < prev_calls) && (calls > 10) )      // if number is weird
 339:    {
 340:        update = 0;                                 // no need to update
 341:        calls = prev_calls;                         // set calls to previous calls
 342:    }
 343:    
 344:    // this handles no call data 
 345:    if( calls == 0 )                                // if no calls
 346:    {
 347:        update = 0;                                 // no need to update
 348:        calls = prev_calls;                         // set calls to previous calls
 349:    }
 350:        
 351:    // the next bit of code runs when a new shift occurs and records the previous maximum calls
 352:    if( calls < prev_calls )                        // if the shift is new
 353:    {   
 354:        maximum[ report ] = prev_calls;             // save last shift maximum
 355:        report++;                                   // increment maximum index
 356:        if( full == 0 )                             // if data set not full
 357:        {   
 358:            if( report >= AVG )                     // if report memory has been filled
 359:                full = 1;                           // set full flag
 360:        }
 361:        if( report >= AVG )                         // if max index needs to be wrapped
 362:            report = 0;                             // wrap the maximum index
 363:        reset();                                    // reset stepper data/positions
 364:    }
 365:}
 366:
 367://- reset ----------------------------------------------------------------------
 368:void reset( void )
 369:{
 370:    // this while/if combo moves the steppers to maximum position
 371:    while( ( (S1<STEPS)||(S2<STEPS)||(S3<STEPS)||(S4<STEPS)||(S5<STEPS) ) )
 372:    {                                               // while not all are max
 373:        if( S1 < STEPS )                            // if S1 is not max
 374:        {
 375:            digitalWrite( stepper_1, HIGH );        // pulse the S1 pin
 376:            S1++;                                   // increment S1
 377:        }
 378:        if( S2 < STEPS )                            // etc
 379:        {                                           // . . . 
 380:            digitalWrite( stepper_2, HIGH );        //
 381:            S2++;
 382:        }
 383:        if( S3 < STEPS )
 384:        {
 385:            digitalWrite( stepper_3, HIGH );
 386:            S3++;
 387:        }
 388:        if( S4 < STEPS )
 389:        {
 390:            digitalWrite( stepper_4, HIGH );
 391:            S4++;
 392:        }
 393:        if( S5 < STEPS )
 394:        {
 395:            digitalWrite( stepper_5, HIGH );
 396:            S5++;
 397:        }
 398:        delay( STEP_DLY );                          // wait 1/2 step
 399:        digitalWrite( stepper_1, LOW );             // set stepper pins low
 400:        digitalWrite( stepper_2, LOW );             // . . .
 401:        digitalWrite( stepper_3, LOW );             //
 402:        digitalWrite( stepper_4, LOW );
 403:        digitalWrite( stepper_5, LOW );
 404:        delay( STEP_DLY );
 405:    }
 406:    for( i = 0 ; i < 50 ; i++ )                     // move across the 50 step
 407:    {                                               // dead zone
 408:        digitalWrite( stepper_1, HIGH );            // . . .
 409:        digitalWrite( stepper_2, HIGH );            //
 410:        digitalWrite( stepper_3, HIGH );
 411:        digitalWrite( stepper_4, HIGH );
 412:        digitalWrite( stepper_5, HIGH );
 413:        delay( STEP_DLY );
 414:        digitalWrite( stepper_1, LOW );
 415:        digitalWrite( stepper_2, LOW );
 416:        digitalWrite( stepper_3, LOW );
 417:        digitalWrite( stepper_4, LOW );
 418:        digitalWrite( stepper_5, LOW );
 419:        delay( STEP_DLY );
 420:    }
 421:    
 422:    S1 = 0;                                         // reset step counts
 423:    S2 = 0;                                         // . . .
 424:    S3 = 0;                                         //
 425:    S4 = 0;
 426:    S5 = 0;
 427:    S1_prev = 0;
 428:    S2_prev = 0;
 429:    S3_prev = 0;
 430:    S4_prev = 0;
 431:    S5_prev = 0;
 432:}
 433:
 434:
 435://- output ---------------------------------------------------------------------
 436:void output( void )
 437:{
 438:    // print out some formatted debugging data to the tx pin
 439:    // Calls AvgMax Pct
 440:    // 12345 12345  100
 441:    if( demo == HIGH )
 442:    {
 443:        Serial.print( "calls avgmax pct" );         // lower case labels
 444:        Serial.print( (char)254 );                  // control mode character
 445:        Serial.print( (char)192 );                  // move to 2,0
 446:        Serial.print( (int)(coeff * 2160) );        // calls this shift
 447:        Serial.print( (char)254 );                  // control mode character
 448:        Serial.print( (char)198 );                  // move to 2,6
 449:        Serial.print( 2160 );                       // demo avgmax = 2160
 450:        Serial.print( (char)254 );                  // control mode character
 451:        Serial.print( (char)205 );                  // move to 2,13
 452:        Serial.print( (int)(coeff * 100 ) );        // percent of max
 453:    }
 454:    else
 455:    {
 456:        Serial.print( "Calls AvgMax Pct" );         // capitalized labels
 457:        Serial.print( (char)254 );                  // control mode character
 458:        Serial.print( (char)192 );                  // move to 2,0
 459:        Serial.print( calls );                      // calls this shift
 460:        Serial.print( (char)254 );                  // control mode character
 461:        Serial.print( (char)198 );                  // move to 2,6
 462:        Serial.print( (int)average );               // averaged maximum
 463:        Serial.print( (char)254 );                  // control mode character
 464:        Serial.print( (char)205 );                  // move to 2,13
 465:        Serial.print( (int)(coeff * 100 ) );        // percent of max
 466:    }
 467:    
 468:    // the following while loops change stepper location
 469:    // by pulsing discrete pins with single-step resolution
 470:    if( update == 1 )
 471:    {
 472:        while( ( (S1_prev<S1)||(S2_prev<S2)||(S3_prev<S3)||(S4_prev<S4)||(S5_prev<S5) ) )   
 473:        {                                           // while steps remain
 474:            if( S1_prev < S1 )                      // if stepper 1 has steps
 475:            {                                       //
 476:                digitalWrite( stepper_1, HIGH );    // pulse stepper 1 pin  
 477:                S1_prev++;                          // increment S1 steps
 478:            }                                       //
 479:            if( S2_prev < S2 )                      // . . .
 480:            {                                       // 
 481:                digitalWrite( stepper_2, HIGH );
 482:                S2_prev++;
 483:            }
 484:            if( S3_prev < S3 )
 485:            {
 486:                digitalWrite( stepper_3, HIGH );
 487:                S3_prev++;
 488:            }
 489:            if( S4_prev < S4 )
 490:            {
 491:                digitalWrite( stepper_4, HIGH );
 492:                S4_prev++;
 493:            }
 494:            if( S5_prev < S5 )
 495:            {
 496:                digitalWrite( stepper_5, HIGH );
 497:                S5_prev++;
 498:            }
 499:            delay( STEP_DLY );                      // delay 1/2 pulse
 500:            digitalWrite( stepper_1, LOW );         // set stepper pins low
 501:            digitalWrite( stepper_2, LOW );         // . . .
 502:            digitalWrite( stepper_3, LOW );         //
 503:            digitalWrite( stepper_4, LOW );
 504:            digitalWrite( stepper_5, LOW );
 505:            delay( STEP_DLY );                      // delay 1/2 pulse
 506:        }   
 507:    }
 508:}
 509:
 510://- scale ----------------------------------------------------------------------
 511:float scale( float value, float min, float max, float out_min, float out_max )
 512:{
 513:    float scaled = ( ( ( value - min ) / ( max - min ) ) * ( out_max - out_min ) ) + out_min;
 514:    return scaled;                                  // fish have scales too
 515:}
 516:
 517://- read_string ----------------------------------------------------------------
 518:void rx_string()
 519:{
 520:    i = 0;
 521:    if( Serial.available() )                        // if a string is buffered
 522:    {    
 523:        while( serialAvailable() )                  // do while data exists
 524:        {
 525:            string[ i ] = Serial.read();            // store a character
 526:            Serial.print( string[ i ] );            // print the character
 527:            i++;                                    // increment string index
 528:        }
 529:    Serial.flush();                                 // flush serial buffer
 530:    }                                           
 531:}
 532:
 533://- cls ------------------------------------------------------------------------
 534:void cls( void )
 535:{
 536:    Serial.print( (char)254 );                      // control character
 537:    Serial.print( (char)1 );                        // clear the display
 538:}
 539:
 540://- splash ---------------------------------------------------------------------
 541:void splash( void )
 542:{
 543:    for( i = 0 ; i < 11 ; i++ )                     // scroll loop
 544:    {   
 545:        cls();                                      // clear the screen
 546:        Serial.print( (char)254 );                  // control character
 547:        Serial.print( (char)( 128 + ( 10 - i ) ) ); // move cursor
 548:        Serial.print( "Nerve" );                    // print 'Nerve'
 549:        Serial.print( (char)254 );                  // control character
 550:        Serial.print( (char)( 192 + ( 10 - i ) ) ); // move cursor
 551:        Serial.print( "Logic" );                    // print 'Logic'
 552:        delay( 150 );                               // 1 'frame' = 150 msec
 553:    }
 554:    Serial.print( (char)254 );                      // control character
 555:    Serial.print( (char)204 );                      // move cursor
 556:    Serial.print( "2007" );                         // print '2007'
 557:    delay( 2000 );                                  // pause 2 seconds
 558:    Serial.flush();
 559:}
 560:
(c)(p) 2012 Cooper Baker · All Rights Reserved · 77279