Source code for the Curilights Controller. See http://www.saccade.com/writing/projects/CuriController/ for details.

Dependencies:   FatFileSystem mbed

This is the source code for the Curilights controller. This lets you interactively control a string of Curilights. It provides a simple click-wheel user interface for changing colors, brightness and behavior. It responds to movement and lighting.

Finished Controller

/media/uploads/isonno/nxp3872_controllerclose.jpg

System Block Diagram

/media/uploads/isonno/blockdiagram.png

UserInterface.cpp

Committer:
isonno
Date:
2013-02-11
Revision:
4:cfef06d8bb96
Parent:
3:0ac64c4ca40f

File content as of revision 4:cfef06d8bb96:

//
// UserInterface - Application specific UI
//

#include "NokiaLCD.h"
#include "UserInterface.h"
#include "LightString.h"
#include "SystemState.h"
#include "SettingsMenu.h"

#include "SDFileSystem.h"
#include <string>
#include <algorithm>
#include <math.h>
#define PI 3.141592653589793

static uint32_t Int2LCDColor( uint16_t intColor )
{
    int r = intColor / 100;
    int g = (intColor % 100) / 10;
    int b = intColor % 10;
    
    return (r << 9) | (g << 5) | (b << 1);
}

void ControllerUI::LightSwitch( bool on )
{
    if (on == fLightsOn)
        return;
        
    fLightsOn = on;
    if (fLightsOn)
        TurnOn();
    else
        fLights->Off();
}

PatternSelector::PatternSelector( CheapLCD * lcd, LightString * lights )
: ControllerUI( lcd, lights )
{
    SDFileSystem sdcard( p5, p6, p7, p8, "sd" );
    DIR * sdDir = opendir("/sd");

    struct dirent *p;
    
    // Read in patterns off of the SD card
    while ((p = readdir( sdDir )) != NULL)
    {
        // Search for files ending in .CRI
        string srcName = string("/sd/") + string(p->d_name);
        size_t pos = srcName.rfind( ".CRI");
        if (pos != string::npos)
        {
            FILE * f = fopen( srcName.c_str(), "r" );
            if (f)
            {
                Pattern pat;
                char line[50];
                uint32_t color;
                
                // Read to the end, or the the end of the frame.
                while (fgets( line, sizeof(line), f) && (line[0] != 'F'))
                {
                    sscanf( line, "%d", &color );
                    pat.push_back( color );
                }
                fPatterns.push_back( pat );
                fPatternNames.push_back( srcName.substr( 4, pos-4 ) ); // Skip "/sd/", Remove ext.
                fclose(f);
            }
        }
    }
    
    // Hack: Since readdir doesn't sort names,
    // force file starting "_" to the top.
    int i;
    for (i = 0; (i < fPatternNames.size()) && (fPatternNames[i][0] != '_'); ++i) {}
    if ((i > 0) && (i < fPatternNames.size()))
    {
        swap( fPatternNames[i], fPatternNames[0] );
        swap( fPatterns[i], fPatterns[0] );
    }
    
    // Maybe the card was swapped, etc...
    if (gSystemState.GetPatternIndex() > (fPatterns.size()-1))
    {
        printf("Reset pattern index\r\n");
        gSystemState.SetPatternIndex(0);
    }
        
    // Need code to make sure "_WEBPAGE.CRI" is in slot zero...
}               

void PatternSelector::Display( bool on )
{
    if (on)
    {
        DrawPattern();
        SetLights();
    }
}

void PatternSelector::SetLights()
{
    int curPattern = gSystemState.GetPatternIndex();
    if (fPatterns.size() > 0)
        for (int i = 0; i < fLights->GetNumLights(); ++i)
            fLights->SetOneColor( fPatterns[curPattern][i % fPatterns[curPattern].size()], i );
}

void PatternSelector::DrawPattern()
{
    int curPattern = gSystemState.GetPatternIndex();

    fLCD->erase();
    if (fPatterns.size() == 0)
    {
        fLCD->draw_glyph_text( WHITE, BLACK, 2, 40, "No Patterns!" );
        return;
    }
    
    fLCD->draw_glyph_text( WHITE, BLACK, 5, 40, fPatternNames[curPattern].c_str() );
    
    int numColors = std::min( (int) fPatterns[curPattern].size(), 12 );
    
    int i, width = 131 / numColors;
    
    for (i = 0; i < numColors; ++i)
        fLCD->clear( Int2LCDColor( (uint16_t) fPatterns[curPattern][i] ), 
                     i*width + 5, 60, ((i+1)*width)+ 3, 80 );
}

void PatternSelector::TurnOn()
{
    SetLights();
}

int32_t PatternSelector::KnobMoved( int32_t step )
{
    int curPattern = gSystemState.GetPatternIndex();
    curPattern += step;
    if (curPattern < 0)
        curPattern = fPatterns.size() - 1;
    if (curPattern == fPatterns.size())
        curPattern = 0;
        
    gSystemState.SetPatternIndex( curPattern );

    DrawPattern();
    SetLights();    
    
    return step;
}

//====================================================

const uint16_t wheelColors[] = {
    770, 760, 750, 740, 730, 720, 
    710, 700, 701, 702, 703, 704, 
    705, 706, 707, 607, 507, 407, 
    307, 207, 107,   7,  17,  27, 
     37,  47,  57,  67,  77,  76, 
     75,  74,  73,  72,  71,  70, 
    170, 270, 370, 470, 570, 670 };
    
#include "WheelCursor.h"
    
const int kNumWheelColors = sizeof(wheelColors) / sizeof(uint16_t);

void ColorSelector::Display( bool on )
{
    if (on)
    {
        fLCD->splash( 1 );
        fLights->SetAllLights( wheelColors[gSystemState.GetSatColorIndex()] );
    }
    DrawCursor( true );
}

void ColorSelector::DrawCursor( bool drawSprite )
{
    const int kScreenSize = 132;
    const int kCursorSize = 13;
    const uint32_t DKGRAY = 0x666;
    
    int curColor = gSystemState.GetSatColorIndex();
    
    double angle = 2*PI / kNumWheelColors;
    double radius = 40;
    
    double vecX = cos(angle * curColor) * radius;
    double vecY = sin(angle * curColor) * radius;
    
    vecX += kScreenSize/2 + 1;   // Screwy epson offset
    vecY += kScreenSize/2;
    
    vecX -= kCursorSize/2;      // Move from center to TR 
    vecY -= kCursorSize/2;      // corner of the cursor
    
    if (drawSprite)
    {
        fLCD->draw_sprite( WHITE, DKGRAY, 
                           (byte)vecX, (byte)vecY, 
                           &WheelCursor_sprt );
        fLCD->clear( Int2LCDColor(wheelColors[curColor]), 50, 50, 82, 82 );
    }
    else
        fLCD->clear( DKGRAY, (byte)vecX, (byte)vecY,
                     ((byte)vecX)+kCursorSize, ((byte)vecY) + kCursorSize );
}

int32_t ColorSelector::KnobMoved( int32_t step )
{
    int curColor = gSystemState.GetSatColorIndex();
    
    DrawCursor( false );
    curColor += step;
    if (curColor < 0)
        curColor = kNumWheelColors-1;
    if (curColor == kNumWheelColors)
        curColor = 0;

    // Must update system state before drawing new cursor
    gSystemState.SetSatColorIndex( curColor );
    DrawCursor( true );
    fLights->SetAllLights( wheelColors[curColor] );
    return step;
}

void ColorSelector::TurnOn()
{
    fLights->SetAllLights( wheelColors[gSystemState.GetSatColorIndex()] );
}

//================================================

void WhiteSelector::SetLights()
{
    int level = gSystemState.GetBrightLevel();
    fLights->SetAllLights( level * 100 + level * 10 + level );
}

void WhiteSelector::Display( bool on )
{
    if (on)
    {
        DrawLevel();
        SetLights();
    }
}

void WhiteSelector::DrawLevel()
{
    fLCD->erase();
    char msg[20];
    sprintf( msg, "Brightness: %d", gSystemState.GetBrightLevel() );
    fLCD->draw_glyph_text( WHITE, BLACK, 5, 40, msg );
}

int32_t WhiteSelector::KnobMoved( int32_t step )
{
    int level = gSystemState.GetBrightLevel();
    level += step;
    if (level < 1)
        level = 1;
    if (level > 7)
        level = 7;

    gSystemState.SetBrightLevel( level );
    DrawLevel();
    SetLights();
    return step;
}

void WhiteSelector::TurnOn()
{
    SetLights();
}

//================================================
LightController::LightController( HomeMenu * parent, CheapLCD * lcd, LightString * lights ) 
: PushKnobUI( lcd ), 
  fParent( parent ), fLights( lights ),
  fSubController( NULL ), 
  fColorSelector( lcd, lights ), 
  fWhiteSelector( lcd, lights ),
  fPatternSelector( lcd, lights )
{
    SetSelector( gSystemState.GetModeSelector() );
    if (fSubController)
        fSubController->TurnOn();
}


int32_t LightController::KnobMoved( int32_t step )
{
    Wake();
    return fSubController->KnobMoved( step );
}

void LightController::SetSelector( ESelector sel )
{
    switch (sel)
    {
    case kColorSelector: fSubController = &fColorSelector; break;
    case kWhiteSelector: fSubController = &fWhiteSelector; break;
    case kPatternSelector: fSubController =&fPatternSelector; break;
    default :;
    }
    
    gSystemState.SetModeSelector( sel );
}

void LightController::KnobPushed()
{
    Wake();
    Display( false );
    fLCD->erase();
    SwitchControl( fParent );
    fParent->Display( true );
}
    
//================================================

HomeMenu::HomeMenu( CheapLCD * lcd, LightString * lights ) 
: UIMenu( lcd, "CuriLights" ), 
  fLightController( this, lcd, lights ),
  fSettingsMenu( lcd, this )
{
    fForcedOff = false;
    fLabels.push_back( string( "Patterns" ) );
    fLabels.push_back( string( "Colors" ) );
    fLabels.push_back( string( "White" ) );
    fLabels.push_back( string( "Lights Off" ) );
    fLabels.push_back( string( "Settings" ) );
}

void HomeMenu::SetLightsOn( bool isOn )
{
    if (fForcedOff)
        return;

    if (isOn != fLightController.AreLightsOn() )
        DoToggleLightsOn();
}

void HomeMenu::DoToggleLightsOn()
{
    fLightController.LightSwitch( ! fLightController.AreLightsOn() );
    ChangeItem( 3, fLightController.AreLightsOn() ? "Lights Off" : "Lights On" );
}

void HomeMenu::DoLightController( ESelector sel )
{
    Display( false );
    fLightController.SetSelector( sel );
    SwitchControl( &fLightController );
    fLightController.Display( true );
}

void HomeMenu::KnobPushed()
{
    // BUG: If display is asleep, should just wake it here.
    switch( SelectedItem() )
    {
    case 0: DoLightController( kPatternSelector ); break;
    case 1: DoLightController( kColorSelector );   break;
    case 2: DoLightController( kWhiteSelector );   break;
    
    case 3: DoToggleLightsOn(); 
            // The ForcedOff state can only be set via the menu
            fForcedOff = !fLightController.AreLightsOn();
            break;
            
    case 4: SwitchTo( &fSettingsMenu );
            break;
            
    default: UIMenu::KnobPushed(); break;
    }
}