Andon light

From Hackerspace ACKspace
Revision as of 10:46, 10 April 2018 by Xopr (talk | contribs) (project stalled needs second midi port fixed)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Project: Andon light
Featured: No
State Stalled
Members xopr
GitHub midi-andon
Description Andon light/signal tower
Picture
Andon in the space.jpg

synopsis

Andon live in the studio

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. The second version was for OHM2013.. The third version was for SHA2017 but the second midi port (for the second mixer) failed and needs to be looked at for the next revision (H-2021)..

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:

andon 1.0

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.

Click here to view the 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);
     }
   }

andon 2.0

Currently being worked on..

It does:

  • do on-board MIDI
  • work stand-alone
  • switch a 220V 2A relay output as well

todo

  • publish both PC c++ midi-to-serial-instructions code as well as the arduino source
  • read values from EEPROM
  • store values to EEPROM
  • add relay that can switch outlet power

Location: at xopr's