Much depends on the PICs you have available, particularly how much and which type of I/O they have, think analog multiplexed ADC.
Sharing rows or columns between sensors and LEDs, like Chris suggests, is often done if sensors are digital like the LEDs I/O, for instance for a matrix of pushbuttons. It's not so evident for mixing analog (photoresistors) and digital (LEDs). What you could share is the I/Os controlling (de)multiplexers if you would need those.
Having a separate controller for sensors and LEDs, like you suggest, could be a good idea, as the extra I/Os may make some multiplexers unnecessary. You'll also need a few lines on each for communication between the two. As I understand it you'll want to start with a simple "action!" signal, but when the interaction becomes more advanced you may want to pass the coordinates of the mug to the other controller, so that its actions can depend on these coordinates. A simple UART will do, but still needs 2 I/Os on each controller (even if you only have communication in one direction).
For the sensors I'm thinking of two CD4051 multiplexers, one for the rows, the other for the columns of a matrix. If your PIC has an analog multiplexer for its ADC you can do with just one CD4051, but this uses a few more I/Os.
Select one of the photoresistors to place in series with a fixed resistor to make a voltage divider, so that you can determine the photoresistor's value with an ADC.
For driving the LEDs you can use a 74HC138 demultiplexer to select one row, and use the low level active output to drive a PNP transistor which will source the current to drive a column. For driving the columns you can use an I/O port of the PIC directly.
Like I said you can share the driving lines for one of the analog multiplexers with those of the 74HC138. Just saves you 3 I/O lines.
One single large battery will be easier to deal with, and will also make it easier to switch all the lights on and off.
To determine how big a battery you need, do the math. Add up the current requirement of all the lights. That's the current the battery must deliver. Now divide the battery capacity by that current to get a time. That's the time, in theory, that the battery can deliver your current for. However, for off the shelf 12V lead-acid car batteries, you want to avoid running them very low because that will cause permanent damage. Either get a larger "car" battery, or a lead-acid battery specifically designed to be able to be drained to nearly empty. Such batteries are called deep cycle, or are sometimes sold as marine batteries.
For example, let's say all the lights together draw 1.3 A (just picking a number out of the air). A 50 Ah car battery can supposedly run the lights for (50 Ah)/(1.3 A) = 38 hours. However, let's consider only half that actually usable for a consumer level car battery, so 19 hours. You'd need to get a bunch of such batteries to last the 112 hours you want. In fact, (112 h)/(19 h) = 6, which is how many 50 Ah "car" batteries it would take.
Since you say you only need to run the lights 9:00-17:00 each day, a better solution is to use a single battery and charge it overnight. Presumably the gallery or whatever is closed during the off hours, so it doesn't matter how it looks with a cord running to a outlet during this time.
Best Answer
Breaking the problem into multiple blocks:
Hardware:
LEDs:
The simplest implementation would involve 16 LEDs. If a smoother transition is desired, 32 or even larger numbers of LEDs could be used, with appropriate LED driver ICs.
Constant current LED driver with built-in PWM dimming support
The Texas Instruments TLC5940 LED driver supports 16 LEDs, each with 12 bit (4096 step) PWM dimming. The PWM duty cycle for each LED is internally generated by the IC, relieving the microcontroller from intense timing and PWM generation effort. The part is controlled by a serial interface, and open source libraries exist for many common microcontrollers and development boards. Outputs are constant current sinks, so one saves on the per-LED current control resistors.
If 32 LEDs or more are desired for a smoother transition, the TLC5940 supports cascading, so a single set of control lines can program the entire array of LED drivers.
If one wants to go way over the top with a smooth track of LED illumination, then the Austria MicroSystems AS1130 132 LED driver, or even their 144 LED driver (AS1119) with its integrated charge pump for driving LEDs from a power rail lower than the forward voltage of your LED, might be of interest.
Microcontroller:
Pretty much any microcontroller capable of driving a serial line (typically SPI) with a reasonably high data rate would suffice. An Arduino Uno, for instance, would be ample.
Power:
Note that while individual LEDs apparently are not power hungry beasts, putting 16 or 144 of them in parallel translates to anywhere from a watt to 10 or more Watts, assuming just normal 20 mA indicator LEDs. Add to this the power consumption of the LED drivers themselves (e.g. 60 mA typical per TLC5940), and the power requirements become pretty hefty.
Software:
Timing:
The microcontroller must be capable of transmitting the LED control packet(s) for all the LED drivers, at better than persistence of vision rates: Preferably more than 20 times per second for a static display, and 200 times per second or better for something that is intended to be moving. Also, the human eye is very sensitive to even minor glitches in a smooth light intensity transition, so the transmission of control data should ideally be driven from a stable counter interrupt, and no other microcontroller task should be allowed to preempt a LED controller refresh.
Intensity calculation:
The computation of a reasonably linear (to the human eye) sequence of PWM duty cycles for a smooth transition involves approximately the following simplified formula:
y = (x ^ 2.5) / k
wherey
is desired intensity for step numberx
,k
is a constant derived from available bit depth (resolution) of the PWM generator.In simple terms: Smaller rise in duty cycle is required at low intensities, bigger jumps at high intensity, to appear linear in transition.
64 distinct intensity steps should suffice for reasonable transition smoothness, 256 if over-the-top is the flavor of the week. For 64 steps mapped to 4096 PWM values (12 bit PWM), the formula above works out to:
PWMvalue = (StepNumber ^ 2.5) x 0.125
It's not perfect but is pretty close.
For 256 steps and 12 bit PWM, the formula is
PWMvalue = (StepNumber ^ 2.5) x 0.00390625
While this computation is trivial enough, for best performance it makes sense to compute the PWM values for each step in advance, and incorporate them as a static array, a lookup table (LUT) within the code. This saves precious cycles during run-time, by retrieving from an array rather than calculating exponential values.
Implementation logic:
In each refresh cycle, update PWM duty cycles for each LED as per the LUT, leaving a gap of say
64 / 4 = 16 steps
between consecutive LEDs. Thus, the first LED will smoothly transition from zero to full within 64 refresh cycles, the second LED will start on the 16th refresh cycle and, the 3rd LED on the 32nd refresh cycle, and so on. For 16 LEDs,64 x 4 = 256 cycles
will leave all LEDs full lit. Calculate the maximum time per refresh by dividing the desired transition time for the full "Tron ring" by 256, and set your refresh interrupt counter accordingly.If you actually implement a Tron ring, please do share a video. The videos of DIY Tron disc light-up implementations currently found on YouTube are way too rudimentary.