Sunday, March 29, 2015

Controlling VMeter on Linux with Java

I bought a VMeter USB MIDI controller on a whim about a year ago (it was on sale).  Since then, it has patiently sat on my desk waiting for me to find a use for it.

I'm not a musician so I didn't buy it for its musical features.  I bought it as an input device and display for my computer.  Since my primary computer runs Linux, I can't run most of the sample programs provided by the VMeter folks.
A few weeks ago I finally sat down to try my hand at getting the VMeter to do something interesting.  Being a Java programmer, the language choice was obvious.  Unfortunately, I didn't find any Java sample code but it didn't prove too hard to adapt the C sample code.
For my first attempt, I wrote a program that changes which LEDs are lit on the VMeter based on a pattern passed in on the command line.  It even allows multiple patterns to be specified on the command line and a delay after displaying each one to make an animated light show.
The second program reads from /proc/stat and /proc/meminfo on Linux and displays the current CPU usage percent and the percent of memory currently used on the VMeter LEDs.  CPU is displayed like a bargraph and memory is displayed as a single LED which is XOR'd with the CPU bar graph.  To make it even more useful I added the ability to set the master volume on my computer (courtesy of the "amixer" command) by touching the VMeter.
I have uploaded both programs to my GitHub repository.

Wednesday, April 23, 2014

Arduino Controlled Light

I mentioned the reasoning behind this project in a previous post, please read it if you can't figure out why anyone would ever want to do this.

Basically, the idea is to have an arduino controlled room light.  The primary goal is to be able to turn the light on and off based on a fixed time table from the moment the light switch is flipped.  A second goal is to fit the components for the build in the same space the light switch would have occupied (basically one side of a dual-gang box).

The first thing you will realize, given the second requirement above, is that a standard arduino will not fit in the space available.  At the time I was planning this, there were several mini arduino compatibles which were a possible fit.  But then I found this article on using a BlinkM MinM as a tiny arduino.  Given the simple goals of this project I should only need a single output pin to control a relay.  The MinM gives me 2 I/O pins and an RGB LED to play with.  Just what the doctor ordered.

I found myself with an extra pin that I wasn't using so I decided to extend the functionality by allowing a visitor to disable the timer.  I toyed with the idea of putting a button on this I/O pin and maybe making the user click in a specific sequence to disable the timer but I quickly discarded the idea.  What else can be done with a single pin?  That's when I remembered the 1-wire family of devices by Maxim Integrated (formerly Dallas Semiconductor).  I decided to use my last pin for a 1-wire iButton reader.

Components I ended up using were:
Here's a sketch of the circuit 



The idea is that the circuit is not even powered until the light switch is turned on.  Once the switch is turned on, it supplies power to the USB power adapter which powers the MinM and the green LED on the iButton reader.  When the MinM powers up it sets pin D to high turning on the relay and the light that's attached to it.  The arduino sketch takes care of turning the relay/light on and off according to a schedule, driving the RGB LED built into the MinM and reading the iButton serial number if present (every iButton has a unique serial number).

I removed the CATV connecter from the Decora filler and made the hole larger until I could fit the iButton reader in its place.  I also glued an angle header to the Decora faceplate so that I could attach and remove the MinM as needed to reprogram it (the MinM has press-fit header holes which keep it securely attached without soldering).  A small hole is drilled in the faceplate that lines up with the LED on the MinM allowing it to shine through.

Decora insert from the back


Decora insert from the front

Everything that will be stuffed in the gang box

After the hardware was sorted out, I wrote an arduino sketch to bring everything together.  Using the OneWire library made everything pretty straight forward.  Since there is no security requirement, the code doesn't actually check the iButton serial number, just the fact that an iButton was connected.

I actually spent much more time playing with the built in RGB LED.  The ATtiny85 that powers the MinM does not support PWM on all of the rgb pins so I wrote a function that fades the LEDs from one color to another in a specified time period.  I use it to provide a nice fade-in/fade-out effect.

Below is the code that is currently running on the light.  Although I program for a living, this is my first Arduino sketch.


#include <OneWire.h>

const int buttonPin = 2;
const int relayPin =  0;
const int redPin =  3;
const int grnPin =  4;
const int bluPin =  1;
const int pollInterval = 100;
const unsigned int minuteMillis = 60000;
const int startReminders = 15;   // minutes until we start flashing reminders

unsigned int elapsedMinutes = 0;          // minutes since start of timing
unsigned int elapsedMillis = 0;           // milliseconds of current second

int reminderElapsed = -1;        // milliseconds since this reminder started

// Serial iButton
OneWire ds(buttonPin);  // on pin 10
byte addr[8];
byte infiniteLives = 0;

// # of items in the reminderDuration array
byte reminderCount = 10;
// When reminders start, we will switch light off for given number of milliseconds
int reminderDuration[] = { 2000, 2000, 2000, 2000, 2000, 3000, 3000, 3000, 3000, 3000 };

void setup() {
 pinMode(relayPin, OUTPUT);
 pinMode(redPin, OUTPUT);
 pinMode(grnPin, OUTPUT);
 pinMode(bluPin, OUTPUT);
 pinMode(buttonPin, INPUT);
 // Switch light ON when powered up
 digitalWrite(relayPin, HIGH);
}

void loop() {
 delay(pollInterval);
 elapsedMillis = elapsedMillis + pollInterval;

 if(infiniteLives == 1) {
   // iButton key previously verified, slow blink
   rgbTransition(0, 0, 0, 0, 0, 99, 500); // fade blue on
   rgbTransition(0, 0, 99, 0, 0, 0, 500); // fade blue off
   delay(5000);
   return;
 }

 // check if iButton is present
 if ( !ds.search(addr)) {
     ds.reset_search();
 } else if ( OneWire::crc8( addr, 7) != addr[7]) {
   rgbTransition(0, 0, 0, 99, 0, 99, 50); // fade purple on
   rgbTransition(99, 0, 99, 0, 0, 0, 50); // fade purple off
   rgbTransition(0, 0, 0, 99, 0, 99, 50); // fade purple on
   rgbTransition(99, 0, 99, 0, 0, 0, 50); // fade purple off
 } else {
   // Acknowledge valid iButton
   rgbTransition(0, 0, 0, 0, 99, 0, 50); // fade green on
   rgbTransition(0, 99, 0, 0, 0, 0, 50); // fade green off
   rgbTransition(0, 0, 0, 0, 99, 0, 50); // fade green on
   rgbTransition(0, 99, 0, 0, 0, 0, 50); // fade green off

   // Turn on lights (in case they were off)
   digitalWrite(relayPin, HIGH);

   infiniteLives = 1;
   ds.reset();
   return;
 }

 if(reminderElapsed >= 0)
   reminderElapsed = reminderElapsed + pollInterval;

 elapsedMillis = elapsedMillis % minuteMillis;
 // Another minute has passed
 if(elapsedMillis == 0) {
   elapsedMinutes++;

   if(elapsedMinutes >= startReminders) {
     if(elapsedMinutes >= startReminders+reminderCount) {
       // Leave light off
       digitalWrite(relayPin, LOW);
       // "Evil" blink (red)
       rgbTransition(0, 0, 0, 75, 0, 0, 500);
       rgbTransition(75, 0, 0, 0, 0, 0, 500);
     } else {
       if(reminderDuration[elapsedMinutes - startReminders] > 0) {
         // start the reminder timer
         reminderElapsed = 0;
         digitalWrite(relayPin, LOW);
       }
       // "Danger" blink
       rgbTransition(0, 0, 0, 75, 75, 0, 500); // fade red+green on
       rgbTransition(75, 75, 0, 0, 0, 0, 500); // fade red+green off
     }
   } else {
     // "All's Well" blink
     rgbTransition(0, 0, 0, 0, 75, 0, 250); // fade green on
     rgbTransition(0, 75, 0, 0, 75, 75, 250); // fade blue on
     rgbTransition(0, 75, 75, 0, 0, 75, 250); // fade green off
     rgbTransition(0, 0, 75, 0, 0, 0, 250); // fade blue off
   }
 }

 if(reminderElapsed >= 0) {
   if(reminderElapsed == reminderDuration[elapsedMinutes - startReminders])
   {
     // disable the reminder timer
     reminderElapsed = -1;
     digitalWrite(relayPin, HIGH);
   }
 }
}
// PWM transition from rgb1 -> rgb2 in 'duration' milliseconds
// rgb values must be 0-99
void rgbTransition(char r1, char g1, char b1, char r2, char g2, char b2, int duration)
{
 for(int step = 0;step < duration;step++) {
   // Each millisecond is a step in the transition
   char rOn = r1+(r2-r1)*float(step)/float(duration);
   char gOn = g1+(g2-g1)*float(step)/float(duration);
   char bOn = b1+(b2-b1)*float(step)/float(duration);
   for(byte i=0;i<3;i++) {
     if(rOn>0)
       digitalWrite(redPin, HIGH);
     if(gOn>0)
       digitalWrite(grnPin, HIGH);
     if(bOn>0)
       digitalWrite(bluPin, HIGH);

     byte j = 0;
     while(j<100) {
       if(rOn == j)
         digitalWrite(redPin, LOW);
       if(gOn == j)
         digitalWrite(grnPin, LOW);
       if(bOn == j)
         digitalWrite(bluPin, LOW);
       j = j + 1;
     }
   }
 }
 digitalWrite(redPin, LOW);
 digitalWrite(grnPin, LOW);
 digitalWrite(bluPin, LOW);
}

Sunday, April 20, 2014

My daughter has a problem...

Don't get me wrong, she's a smart, happy, relatively obedient kid with many friends and interests.  But she has a tendency to zone out.  When she zones out, she has no perception of the passage of time.  She goes into a state where she exists outside of time.  Well, maybe I exaggerate, but it sure feels that way to a couple of impatient parents.

This trance-like state is especially likely to strike while she's in the bathroom ready to take a shower.  It's become a tradition to send her upstairs for a shower and then waiting an agonizing 30 minutes while not a single sound emerges from the bathroom.  Then a concussive shout from one of her parents kicks the gears back into motion and it triggers an immediate flush and the sound of the shower being turned on.

The shower of course is another opportunity for her to continue her meditation which then results in more shouting and gnashing of teeth.  After many threats and attempts at rewards or punishment, we have come to the conclusion that she does not have any form of control over these episodes.

What is a parent to do?

During one of these incidents a spark of inspiration struck me.  We needed a way to snap her out of these trances without using one of her angry parental units.  A machine would be able to do this without getting angry.  She needed a sort of pacemaker for her brain to get her functioning again.  After discarding the idea of actual wires connected to her brain and zapping her back into action, I came up with an alternate solution.

Assuming she was awake during these episodes, I could use the bathroom lights to snap her back into reality.  I would use an arduino to control her bathroom lights.  It would work as follows.  When she turns on the bathroom lights she gets 15 minutes of uninterrupted illumination.  After 15 minutes the lights start to blink off for a couple of seconds every minute.  After 20 minutes, the lights start to blink off for 5 seconds every minute.  After 25 minutes the lights go off and don't come back on.

The actual implementation is not foolproof but fills its intended purpose.  Basically, the whole contraption is powered on when a standard light switch is turned on and starts the sequence described previously.  Turning the light switch off and on again will of course restart the timer at zero but this is not a problem in practice, since the intended purpose of the device is to bring a child back into reality and not to enforce an intricately timed light show.

It's been 6 months since this was implemented.  You may wonder, what has been the result of the experiment?  Well, we never have to shout anymore.  Shower time is usually under half an hour and there have been some unexpected benefits!  We have substantially diminished our daughters fear of the dark and she has begun to develop some rudimentary echolocation abilities.


I'll include build details in another post.