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
System Block Diagram
Diff: UserInterface.cpp
- Revision:
- 0:6da5625a6946
- Child:
- 1:d1da77023e6a
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UserInterface.cpp Thu Dec 29 01:59:53 2011 +0000 @@ -0,0 +1,373 @@ +// +// UserInterface - Application specific UI +// + +#include "NokiaLCD.h" +#include "UserInterface.h" +#include "LightString.h" +#include "SystemState.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 ), fLCD( lcd ), 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(); + fParent->Display( true ); + SwitchTo( fParent ); +} + +//================================================ + +HomeMenu::HomeMenu( CheapLCD * lcd, LightString * lights ) +: UIMenu( lcd, "CuriLights" ), + fLightController( this, lcd, lights ) +{ + 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 ); + SwitchTo( &fLightController ); + fLightController.Display( true ); +} + +void HomeMenu::KnobPushed() +{ + 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; + + default: UIMenu::KnobPushed(); break; + } +}