Andon light

From Hackerspace ACKspace
Revision as of 20:41, 2 September 2011 by Xopr (talk | contribs)
Jump to: navigation, search
Project: Andon light
Featured:
State Completed
Members xopr
GitHub No GitHub project defined. Add your project here.
Description Andon light/signal tower
Picture
No project picture! Fill in form Picture or Upload a jpeg here

synopsis

Andon in the space

An andon light is a color-coded visual display for process state control; in short: On-Air light

The first version is completed, and used in the Binary Voice studio at the CCC2011.

It is now located in the space as mood-light

implementation

I used a light tower/andon light which a good friend Spert modified to fit 6 leds in it.

prototype

Since the media plugin doesn't work, you have to open the file manually: Media:andon_prototype.mov

first version

The first version listens on a serial port and change the lights depending on the data it receives. On the PC, an application listens to the midi signals and above certain channel thresholds, it will send out the correct signal data over the serial port.

Here is the current Arduino source code:

  //#define _DEBUG
  
  enum SwitchMode
  {
    Slow,
    Normal,
    Fast  
  };
  
  enum LoopMode
  {
    SingleShot,
    Bounce,
    Loop  
  };
  
  enum Pins
  {
    Red = 0,
    Orange,
    Green,
    Blue
  };
  
  const int pTelephone = 15;
  
  void DebugPrint( char* _strInfo )
  {
  #ifdef _DEBUG
      Serial.println( _strInfo );
  #endif
  }
  
  void DebugPrint( int _nInfo )
  {
  #ifdef _DEBUG
      Serial.print( _nInfo );
      Serial.print( ", " );
  #endif
  }
  
  
  // The analogue have 17 values due to inverse square law on led brighness
  static const byte MAX_INDEX = 16;
  static const byte s_fadeValues[]      = { 255, 180, 128,  90,  64,  45,  32,  23,  16,  12,   8,   6,   4,   3,   2,   1,  0 };
  static const byte s_slowBlinkValues[] = { 255, 255, 255, 255, 255, 255, 255, 255,   0,   0,   0,   0,   0,   0,   0,   0,  0 };
  static const byte s_fastBlinkValues[] = { 255, 255, 255, 255, 255,   0,   0,   0, 255, 255, 255, 255, 255,   0,   0,   0,  0 };
  
  // the colors are red, orange, green and blue respectively
  // serial light instructions
  static const byte s_onCommand[]  = { '!', '@', '#', '$' };
  static const byte s_offCommand[] = { '1', '2', '3', '4' };
  
  // output pins
  static const byte s_pins[] = { 3, 5, 6, 9 };
  
  // values (off)
  volatile byte g_values[] = { MAX_INDEX, MAX_INDEX, MAX_INDEX, MAX_INDEX };
  
  // step
  volatile byte g_step[] = { 0, 0, 0, 0 };
  
  // light operation modes
  volatile SwitchMode g_eSwitchMode[] = { Normal, Normal, Normal, Normal };
  volatile SwitchMode g_eCurrentSwitchMode = Normal;
  
  volatile LoopMode   g_eLoopMode[] = { SingleShot, SingleShot, SingleShot, SingleShot };
  volatile LoopMode   g_eCurrentLoopMode = SingleShot;
  
  void setup( )
  {
    Serial.begin( 9600 );
    
    // make it output
    pinMode( s_pins[ Red    ], OUTPUT );
    pinMode( s_pins[ Orange ], OUTPUT );
    pinMode( s_pins[ Green  ], OUTPUT );
    pinMode( s_pins[ Blue   ], OUTPUT );
    
    /*
    pinMode( pTelephone, INPUT );
    digitalWrite( pTelephone, LOW );
    
    pinMode( 14, INPUT );
    digitalWrite( 14, LOW );
    
    pinMode( 20, INPUT );
    digitalWrite( 20, LOW );
    */
    
    // wait to make sure serial is initialized on the host
    delay(1000);
    
    DebugPrint( "initialization done, starting demo mode" );
    
    demoMode( );
  }
  
  void loop( )
  {
    byte idx;
    byte nCurrentPinValue;
    
    // check if data has been sent from the computer:
    if (Serial.available( ))
    {
      byte input;
      // read the most recent byte (which will be from 0 to 255):
      input = Serial.read( );
      
      switch ( input )
      {
     // switch mode
     case 's':
     case 'S':
       DebugPrint( "switching to slow mode" );
       g_eCurrentSwitchMode = Slow;
     break;
     case 'n':
     case 'N':
       DebugPrint( "switching to normal mode" );
       g_eCurrentSwitchMode = Normal;
     break;
     case 'f':
     case 'F':
       DebugPrint( "switching to fast mode" );
       g_eCurrentSwitchMode = Fast;
     break;
     // loop mode
     case 'o':
     case 'O':
       DebugPrint( "switching to single shot mode" );
       g_eCurrentLoopMode = SingleShot; // aka once
     break;
     case 'b':
     case 'B':
       DebugPrint( "switching to bounce mode" );
       g_eCurrentLoopMode = Bounce;
     break;
     case 'l':
     case 'L':
       DebugPrint( "switching to loop mode" );
       g_eCurrentLoopMode = Loop;
     break;
     
     // version
     case 'v':
     case 'V':
     case 'h':
     case 'H':
       Serial.println("xopr's Andon Light v0.2");
       Serial.println("");
       Serial.println(" Usage:");
       Serial.println("  [snf]?[obl]?([1234]|[!@#$])");
       Serial.println("");
       Serial.println(" Switch modes:");
       Serial.println("  [s]low");
       Serial.println("  [n]ormal");
       Serial.println("  [f]ast");
       Serial.println("");
       Serial.println(" Loop modes:");
       Serial.println("  [o]ne shot");
       Serial.println("  [b]bounce");
       Serial.println("  [l]loop");
       Serial.println("");
       Serial.println(" Lights:");
       Serial.println("  {1!}: Red");
       Serial.println("  {2@}: Orange");
       Serial.println("  {3#}: Green");
       Serial.println("  {4$}: Blue");
       
       break;
     
     // assume it's a switch command
     default:
       for ( idx = 0; idx < 4; idx++ )
       {
         if ( input == s_onCommand[ idx ] )
         {
           // apply the optionally prefixed or default modes
           applyModes( idx );
           
           // set the stepping, so we know that we need to update the output port
           g_step[ idx ] = -1;
           
           // special mode: normal single shot means just toggle the state
           if ( g_eSwitchMode[ idx ] == Normal && g_eLoopMode[ idx ] == SingleShot )
           {
             DebugPrint( "ultra fast on!!1`" );
             g_values[ idx ] = -g_step[ idx ];
           }
           
           // since we found our character, break out of the loop
           break;
         }
         if ( input == s_offCommand[ idx ] )
         {
           // set the stepping, so we know that we need to update the output port
           g_step[ idx ] = 1;
           
           // special mode: normal single shot means just toggle the state
           if ( g_eSwitchMode[ idx ] == Normal && g_eLoopMode[ idx ] == SingleShot )
           {
             DebugPrint( "ultra fast off!!1`" );
             g_values[ idx ] = MAX_INDEX - g_step[ idx ];
           }
           
           // apply the optionally prefixed or default modes
           applyModes( idx );
           
           // since we found our character, break out of the loop
           break;
         }
       } // for
     break;
      } // switch input
    } // serial.available
    
    // now do the stepping check/iteration on the lights
    for ( idx = 0; idx < 4; idx++ )
    {
      if ( g_step[ idx ] )
      {
        
        /*
        b0rken
        // update the current value, if needed
        if ((g_step[ idx ] < 0 && g_values[ idx ] <= 0)
         || (g_step[ idx ] > 0 && g_values[ idx ] >= MAX_INDEX ))
        {
          g_step[ idx ] = 0;
          continue;
        }
        */
        
        g_values[ idx ] += g_step[ idx ];
        
        // reached the boundaries? update the stepping (and prolly the loopmode)
        if ( g_values[ idx ] <= 0 || g_values[ idx ] >= MAX_INDEX )
        {
          // reset stepper if we're done with single shot or return
          if ( g_eLoopMode[ idx ] == SingleShot )
          {
            DebugPrint( idx );
            DebugPrint( " is done" );
            g_step[ idx ] = 0;
          }
          else
            // invert stepping
            g_step[ idx ] = -g_step[ idx ];
     
          // after a bounce, loopmode changes into single shot
          if ( g_eLoopMode[ idx ] == Bounce )
          {
            DebugPrint( idx );
            DebugPrint( " bounced to single shot mode" );
            g_eLoopMode[ idx ] = SingleShot;
          }
        }
        
        switch ( g_eSwitchMode[ idx ] )
        {
     case Slow:
       nCurrentPinValue = s_fadeValues[ min( g_values[ idx ], MAX_INDEX ) ];
       break;
     case Normal:
       nCurrentPinValue = s_slowBlinkValues[ min( g_values[ idx ], MAX_INDEX ) ];
       break;
     case Fast:
       nCurrentPinValue = s_fastBlinkValues[ min( g_values[ idx ], MAX_INDEX ) ];
       break;
        }
        
        analogWrite( s_pins[ idx ], nCurrentPinValue );
      } // if stepping
    } // for
    
    /*
    if ( digitalRead( pTelephone ) == LOW )
      digitalWrite( s_pins[ Blue ], HIGH );
      
    if ( analogRead( pTelephone ) >= 1022 )
      digitalWrite( s_pins[ Blue ], HIGH );
    */
    
    /*
    Serial.println( analogRead( pTelephone ) );
    Serial.println( analogRead( pTelephone ) );
    Serial.print( " " );
    Serial.print( analogRead( 14 ) );
    Serial.print( " " );
    Serial.print( analogRead( 15 ) );
    Serial.print( " " );
    Serial.print( analogRead( 16 ) );
    Serial.print( " " );
    Serial.println( analogRead( 17 ) );
    */
    
    delay( 50 );
  }
  
  void applyModes( byte _idx )
  {
    
    // apply the current modes
    if ( g_eSwitchMode[ _idx ] != g_eCurrentSwitchMode || g_eLoopMode[ _idx ] != g_eCurrentLoopMode )
    {
      g_eSwitchMode[ _idx ] = g_eCurrentSwitchMode;
      g_eLoopMode[ _idx ]   = g_eCurrentLoopMode;
      DebugPrint( _idx );
      DebugPrint( " has new mode applied" );
    }
    
    // reset the current modes
    g_eCurrentSwitchMode = Normal;
    g_eCurrentLoopMode   = SingleShot;
  }
  
  void demoMode( )
  {
    byte idx;
    
    // set values and stepping
    g_values[ Red ] = 17;
    g_values[ Orange ] = 13;
    g_values[ Green ] = 10;
    g_values[ Blue ] = 6;
    
    for ( idx = 0; idx < 4; idx++ )
      g_step[ idx ] = -1;
    
    while ( !Serial.available( ))
    {
      for ( idx = 0; idx < 4; idx++ )
      {
        g_values[ idx ] += g_step[ idx ];
        
        if ( g_values[ idx ] >= 30 )
        {
     //DebugPrint( idx );
     //DebugPrint( ": value >= 30 == off" );
     g_step[ idx ] = -1;
        }
        else if ( g_values[ idx ] <= 0 )
        {
     //DebugPrint( idx );
     //DebugPrint( ": value <= 30 == on" );
     g_step[ idx ] = 1;
        }
        
        analogWrite( s_pins[ idx ], s_fadeValues[ min( g_values[ idx ], MAX_INDEX ) ] );
      }
      
      delay( 50 );
    }
    
    DebugPrint( "end of demo mode. cleaning up" );
    
    for ( idx = 0; idx < 4; idx++ )
    {
      g_step[ idx] = 0;
      g_values[ idx ] = MAX_INDEX;
      digitalWrite( s_pins[ idx ], LOW);
    }
  }

future version(s)

Create an andon light which has a MIDI port on-board where all the logic is inside the Arduino/AVR with channel thresholds on the internal ROM.

todo

  • publish both PC c++ midi-to-serial-instructions code as well as the arduino source