Wednesday, July 27, 2011

Is it dawn yet? How shall we know?

Those of you who've been following the development of the MCU-based ANLC will probably remember that I turned to a micrcocontroller since I want to switch the load off not at the ambient light level (corresponding sensor voltage) at which we want to turn on the load, nor at the load turn-on ambient light level + some hysteresis, but rather at the very crack of dawn, which corresponds to a light level significantly less than that at load turn-on. So you might be wondering what algorithm I'm currently using to implement that.

With all other factors (clouds, city lights, etc.) kept constant, one characteristic of dawn is that the light level rises evenly and continuously (thanks to the very stable rotation speed of the earth and nearly constant light output of the sun). And this increase will not exceed a certain dV/dt--which we've measured (at least in my particular location in the globe which happens to be 15° north of the equator) to be almost 6mV/s (@Vdd = 5V).

So one way of ruling out other light sources is to look at whether dV/dt > 0 but < 6mV/s and whether this is sustained over time. If it's currently very dark (night time) and these conditions are met then there is a good probability we are witnessing dawn/sunrise. If not then there is a good chance the light source isn't the sun.

In addition we'd want to monitor for the above conditions as close to actual dawn as possible. We don't, for instance, want to check after just a couple of hours after dusk. And speaking of time of day, our circuit doesn't actually know what time of day it really is. For instance just because ambient light level has dropped below the trip point doesn't mean it really is dusk. It may be midday but the sky is so heavy with dark nimbus clouds that the light level has dipped to a point that the load has been turned on. The algorithm needs to take this into consideration.

One way of gaining confidence that it really is nighttime is to monitor the amount of time the load has been on. If the shortest night during the entire year is, say 10 hours, then we can be fairly sure that it's night if the load has been on for at least 8 hours. We can thus wait 8 hours after the load has been on before beginning to check for dawn conditions.

We can also monitor the sensor voltage to find out if it's really nighttime, but the night time reading/value will depend on where we install the sensor and how much artificial light sources will be polluting it. As I mentioned before I placed the sensor on the roof and shielded it on the sides to minimize the effects of streetlights, billboard lights, et al.

Besides just waiting x hours to begin monitoring for dawn dV/dt, we also want to find out the maximum light level for an hour or two before the time we begin checking for dawn because we only want to start applying our dawn dV/dt criteria when ambient light level exceeds this value. Given that this is the maximum ambient light level close to dawn, there is a greater probability that light whose intensity exceeds it and which keeps increasing but has a rate of increase less than the max dV/dt is in fact indicative of the first rays of the sun trickling in and not some other light source.

While I've implemented the above set of detection conditions for several days now--and so far so good--I imagine they do not provide a foolproof way of determining the onset of dawn. For example, if our firmware is already monitoring for dawn conditions and clouds begin rolling in, gradually reflecting more of the city lights thus increasing the sky light level and if the clouds move in at just the right rate so that max dV/dt isn't exceeded, then our firmware will be fooled into switching the load off.

One way of further improving the system is to add another sensor. We set the sensors up such that one receives light mainly from the eastern part of sky while the other from the western half (this can be as simple as having the LDRs side by side with an opaque board between them to block light from the eastern/western side of the sky from falling on the west/east LDR). We can then monitor for differences in light levels detected. Surely the eastern part of the sky will be brighter at dawn. As a bonus we can use this setup to detect dusk as well.

For now, however, I'm sticking to a simple single-LDR sensor. There won't be need to make it any more complex if the algorithm works or a better algorithm can improve dawn detection.

Monday, July 25, 2011

A more decent measurement of dawn and dusk dV/dt

I previously estimated the maximum dV/dt of the MCU-based ANLC sensor during dusk and dawn at 4.5mV/s, but I want to have a more accurate figure. Because I don't have the proper test gear, I had to make do with a DMM and a stopwatch.

I decided to keep dV constant at 0.1V and record the time it took for the sensor voltage to increase/decrease by that amount. Thus what I was recording was in fact dt/dV. It would then be a simple matter of getting the reciprocal to obtain dV/dt.

The stopwatch of my Nokia cell phone has a "split timing" feature that allows the user to press the button and have a reading without actually stopping the stopwatch. Unfortunately, while using it I found out that it has a limit of 20 split timing readings. So as you will see, there are breaks in the data and graph. Those are the times when I had to reset the stopwatch. Too bad I just discovered this online split timer and countdown timer. Would've been great to use it.

The July 23 readings were a spur of the moment thing. I didn't actually plan to record the values. And so data on that date are quite limited.

Enough intro. Here's the spreadsheet with all the data, computations and graphs.

It's clear from the table and graphs that the maximum dV/dt is almost 6mV/s. Two things of interest: 1. The curves for dawn and dusk apparently don't have the same shape. Wonder if more data will even out the differences or if this difference is intrinsic. 2. The July 24 dusk graph is shifted downwards compared to the previous day's. This is probably because it was rather overcast on the 24th. That dV/dt can be attenuated by cloud cover needs to be noted.

Friday, July 22, 2011

Digital filter for auto night light

In any automatic night light circuit that's meant to switch on perimeter security lights, the time period we are most interested in is dusk and dawn. We want the lights to be switched on at dusk and turned off at dawn. (Whether the lights are turned on/off earlier or later is dependent on the user.) During the night we want the lights on regardless of lightning flashes or car headlamps shining on the sensor or bright city lights being reflected by the clouds or a full moon or what have you. During the daytime we want the lights off regardless of clouds rolling in and out and thus momentarily blocking the sun, or a very overcast day, or a heavy downpour. Of course we would want the lights turned on even during the middle of the day if for some reason utter darkness descended such as during a (near) total solar eclipse, or--if we wanted to test the circuit--if we covered the sensor with some opaque material.

I've discussed the following light sensor here and here. Just a summary of how it works: R1 and LDR form a voltage divider. When the voltage across LDR is greater than across the 10uF capacitor, the cap gets charged via D1. When voltage across the cap is greater than across LDR, the capacitor discharges via R2. Given how D1 shunts R2 during charging and given that R1 has a much lower value than R2, the net result is that charging rate is faster than discharge rate. This means that when there is an abrupt and large increase in the level of ambient light (LDR resistance decreases) the voltage across the cap decreases slower compared to when there is sudden and large decrease in light level (LDR resistance increases). In other words, the circuit has a greater inertia to positive changes in light level than to negative changes in light level. During daytime, assuming the sensor were located outdoors and were we to shroud it with a black opaque bag, the voltage output would within seconds rise close to VDD. Were we then to take off the bag, the voltage would drop back to near ground level but a slower rate than when darkness fell upon the LDR.

Recently I provided an  update on the MCU-based ANLC I'm testing. It's still a work in progress but I thought I'd provide more information on the algorithm I'm using to try and mimic the asymmetric filtering action of the analog circuit above.

In the MCU version of the ANLC the LDR and fixed resistors have switched places. Sure we can retain the fixed resistor as the pull-up, but I find it more intuitive associating a low voltage sensor output with darkness and near VDD output as bright light condition.

One way of implementing a low pass filter digitally is to compute for the moving average (MA). We can read the ambient light level at fixed time intervals and store the values in a one-dimensional array. Every time a new reading is performed the oldest reading is discarded and the new reading stored. The readings are summed and average computed.

One problem with the MA filter is that it will not provide the asymmetric filtering. As the graphs below show, an MA filter reacts the same way whether the light level is increasing or decreasing. Another characteristic of the MA filter is that a large number of readings are required to sufficiently attenuate light level changes that have a high dV/dt (change in voltage over change in time). Having a large number of readings is analogous to having large capacitance. A large cap takes longer to charge/discharge. More readings means there is more inertia to new readings. But lots of stored readings translates to memory guzzling, specially because 16-bit values are going to be stored. Because it can't meet the specs we have to conclude that the MA filter is unsuitable for this application.

Because the charge and discharge rates of the capacitor in the analog circuit above is what provides the filtering action, we can implement this digitally by limiting the dV/dt of light level readings. Here dt translates to the interval between reads by the analog-to-digital converter. If this interval is fixed then we need only deal with dV. Because we want to have more inertia against increasing light levels we can limit the dV when it is positive much more strongly when dV is negative. This is easily done in firmware by comparing the previous and current light level readings. If the difference exceeds the maximum positive (or negative dV), then we only store a value equal to the previous reading plus the positive dV (or minus the negative dV).


dV+ = maximum permitted change in voltage if voltage is increasing
dV- = maximum permitted change in voltage if voltage is decreasing
Vi = current voltage reading -- will be adjusted to meet max dV
Vi-1 = previous voltage reading -- already adjusted to meet max dV

and assuming that Vi contains the latest raw/unmanipulated ADC reading, the algorithm for what we can call an asymmetric dV attentuation or ADVA filter is as follows (written in pseudo C):

if (Vi > Vi-1 && Vi - Vi-1 > dV+)
  Vi = Vi-1 + dV+

if (Vi < Vi-1 && Vi-1 - Vi > dV-)
  Vi = Vi-1 - dV-

Manipulation of the light reading is performed only if and when either of the dV limits are exceeded. Otherwise the raw reading is accepted as is.

From a record of ambient light levels during dawn and dusk we can determine that the maximum dV/dt = ~4V / 15min = ~4.5mV/s @VDD = 5V. Therefore, assuming dt = 1s, we can let  dV+ equal to some value close to 4.5mV while  dV- can be much larger in order to create the asymmetry.

To see how the MA and ADVA compare with one another I did a simulation using a spreadsheet. Check it out to see the numerical values used. Below I've pasted the graphs from the simulation. For the MA the number of readings averaged = 16. For the ADVA, dV+ = 0.01V, dV- = 0.25V. These dV values are the ones I'm currently using in a bench test.

The above simulates night condition where a transient bright light condition occurs (eg. a flashlight is shone directly at the sensor for many seconds). The light level is the blue rectangular pulse. The red curve is the MA response, while the yellow-orange curve is the ADVA output. Compared to the ADVA the MA has a much steeper slope. Because the MA averages 16 readings, after a time interval of 16, the MA output = to the sensor output. The ADVA on the other hand barely budges from its initial value.

Now let's say the sensor output voltage at which the load is turned on is 1V and the hysteresis band = 1V (and so the load turn-off voltage = 2V). With the MA the load will be turned on and then off with this particular burst of light on the sensor. The ADVA meanwhile will be immune to it. For the transient to get the ADVA to trip the load, it has to be sustained for a much much longer time.

Note the MA and ADVA waveforms: triangular and sawtooth, respectively. As we will see the waveforms will change depending on the input signal.

The above simulates daytime condition with the sensor suddenly immersed in darkness (eg. it was covered with a thick black plastic bag). The results are interesting. The MA and ADVA overlap each other during the downward slope. But this is just a coincidence. For a different number of readings for the MA or a different  dV-, each of them will have a different negative slope. Nevertheless, with either filter the load will be turned on (assuming, as before that the trip levels are 1V and 2V). But when the light level returns to "normal," you can see how the ADVA takes its time rising. This means that it will be a very long while (about a hundred seconds in an actual bench test with dt = 1sec and dV+ and  dV- as per simulation values) before the load is switched off. 

Any light level increase with a rate of change greater than those during dusk or dawn will be attenuated by the ADVA. What surprised me in the above is how a triangular input to the MA results in a (quasi?) Gaussian waveform.

Because the rate of decrease in the light level above is less than dV- the ADVA tracks the light level while it is decreasing and thus we see the waveforms overlapping. However, the two part ways when light level begins to increase.

Out of curiosity I tried out geometric changes in light level. The one above and below use a factor of 1.05. The ADVA tracks the increase in light level until the point where it exceeds  dV+.

Because the negative rate of change of the light level is well below dV- the light level and ADVA waveforms overlap.

Wednesday, July 13, 2011

Microcontroller-based automatic night light circuit

While discussing an all-linear automatic night light circuit I mentioned that I was testing an MCU-based ANLC. Well, I've been tweaking the firmware since. The other day I installed the sensor outdoors and have connected the circuit to control 220VAC loads.

Last night the circuit got fooled when clouds moved in at around 2am and reflected sufficient city lights and streetlights that the MCU thought it was already the beginning of dawn and switched off the load. This is because the software algorithm is such that a 10-bit ADC change of just 0x00A from the average dark ambient light level is interpreted as dawn. Competent programmer that I am, the firmware also includes a conditional which turns the load back on again should the light level drop back to the average dark level. And some half hour or an hour later (not sure because I went back to sleep before waking up again some time later) it did actually did return to dark level conditions. A sonalert buzzer is temporarily hooked up such that it sounds when the load (perimeter security lights) is off and as far as I know (sleepy head that I am) the next time the load was switched off by the circuit was around 5am--true dawn.

Earlier that same evening at around midnight while remotely monitoring the voltage output of the sensor ("ldr" in the schematic) with a DMM I was surprised to see the numbers climbing rapidly. The output had already settled at ~20mV but then increased quickly within minutes and peaked at ~310mV. I had to look out the window just to make sure I wasn't witnessing a circuit glitch but that in fact ambient light level was shooting up. And true enough the sky had turned orange and bright--from the cloud cover. The circuit did not turn the load off because the firmware was using a different hysteresis value at that time. Only after the load has been on for six hours does it use the hysteresis value of ten (the 10-bit ADC value mentioned above). By 2am this six hours had already elapsed.

Because of the detection sensitivity used, the light sensor circuit is located on the roof to minimize being affected by artificial lights. The LDR faces directly up (zenith) and is housed in a translucent plastic box. The box is shielded on the sides to prevent streetlights and lights from nearby buildings and billboards from unduly affecting the sensor. The very high impedance output of the sensor requires a unity gain buffer. The LDR used has a resistance of >30Mohms in complete darkness.

On the main control board, a low pass filter comprised of RLPF and CLPF (cut off frequency = 1/(2πRC) = 1/(6.28*100K*0.1uF) = 16Hz) attenuates high frequency noise which could be caused by EMI particularly those from lightning. A unity gain buffer is used to provide a low impedance signal for the MCU. High value load resistor RL is used because in the event that the cable to the sensor breaks, the input of the LPF and buffer will not be left floating. In such an event RL grounds the input, fooling the MCU to think that it's nighttime and thus turning on the load. This serves to alert the user (in the daytime) of a malfunction.

PB is used to select the light level at which load will be turned on. The user waits until the ambient light level is at the point where s/he wants the load switched on. At that moment PB is pressed. The circuit stores the light level in EEPROM and uses this value until it is changed. Pressing and holding down PB for a second resets the value to its minimum (I arbitrarily set it ADC value 0x064)--meaning load will be turned on only when it's really dark. LY is the indicator light for PB. It flashes briefly when PB is pressed (falling edge detected) and it blinks on and off for 1.5s when the trip level has been reset to minimum. A debouncing routine is used to, well, debounce PB, detect falling/rising edges, and monitor how long PB has been depressed.

REL1 and REL2 are mini relays with 24VAC coil. They are switched using a MOC3021 optoisolator employing a snubber circuit. REL1 and REL2 are at different locations. Using low voltage to control the relays translates to safety and perhaps even less expensive wires.

Tuesday, July 12, 2011

Triac optoisolator directly driving an inductive load

As far as I know the MOC301x and MOC302x series of triac optoisolators are intended to drive power triacs and not meant to drive loads directly. Indeed, I just checked Fairchild's datasheet and it says so explicitly: "The optoisolator should not be used to drive a load directly. It is intended to be a trigger device only." But then Motorola, I mean On Semiconductor, tells us that, "Zero-crossing optocouplers can be used to switch inductive loads at currents less than 100 mA (Figure 24)" [p.162].

What I have in my parts bin are a number of MOC3021. Since these can pass around 100mA [p.124] continuously, with a peak current of 1000mA (pulse width of 1ms, 120 pulses per second), there shouldn't be a problem if the load does not exceed these maximums. The MOC3021 is a random-phase and not zero-crossing optoisolator but I figure if I just close my eyes, ignore the difference and wish upon a star then everything will be alright.

The load I need to switch are 24VAC mini relays which in turn will switch 220VAC loads. Measured maximum current of the relay @24volts is 46mA. Connecting a 24VAC source and a relay in series with the triac output presented no problems--turning the MOC3021's LED on and off switched the relay. I then wondered if the optoisolator could drive two relays in parallel. They switched on fine, but when I powered down the LED the relays remained on. They just wouldn't turn off.

To try and remedy this I placed a 0.1uF ceramic capacitor across the triac. That immediately got rid of the latching. I tweaked the snubber and replaced the cap with a 0.01uF and a 1Kohm 1/4W resistor. No latching problems. However, decreasing capacitance to 1nF or increasing resistance to tens of kilohms reduced snubbing to a point that latching would occur.

The value of the snubbing resistor should probably be close to the resistance of the load. Given that the coil resistance of each relay is around 180ohms, snubbing resistor should be around 91 or 100ohms.

Thursday, July 7, 2011

Vexing silicon bug on the PIC12F1822

Been having a problem with a firmware which uses the analog-to-digital converter (ADC) of the PIC12F1822 to read the buffered voltage output of a sensor. The circuit works for some time but then minutes or hours later goes amok. I've gone over the firmware half a dozen times and I can't find anything wrong. (That of course is no guarantee that the program has no coding errors. I may just be "blind" to the booboos I've committed.) 

While testing the circuit and observing the hiccups, it looked to me--among other possibilities--that the watchdog timer (WDT) was timing out--that it wasn't being cleared and thus the MCU was being reset. Sure enough when I disabled the WDT the MCU would hang and the push button and LED would fail to respond. Something was very wrong indeed. It was then that it occurred to me to check the silicon errata sheet for this device.

When I saw item number two on the rather obscenely long list of bugs I had an "Oh shoot!" moment. According to Microchip
Under certain device operating conditions, the ADC conversion may not complete properly. When this occurs, the ADC Interrupt Flag (ADIF) does not get set, the GO/DONE bit does not get cleared and the conversion result does not get loaded into the ADRESH and ADRESL result registers.
Two workarounds are listed:
Method 1: Select the dedicated RC oscillator as the ADC conversion clock source, and perform all conversions with the device in Sleep.

Method 2: Provide a fixed delay in software to stop the A-to-D conversion manually, after all 10 bits are converted, but before the conversion would complete automatically. The conversion is stopped by clearing the GO/DONE bit in software. The GO/DONE bit must be cleared during the last ½ TAD cycle, before the conversion would have completed automatically. Refer to Figure 1 for details.
The firmware uses mikroC's built-in ADC function and checking the assembly of the compiled program it's clear that the function is polling the GO/DONE bit to determine whether conversion is finished or not. If the silicon bug is indeed being triggered then when it does strike this polling would go on ad infinitum since the bit would never change state. To find out if this ADC bug is the cause of my woes I had to write my own ADC function to implement one of the workarounds above.

Well the part about clearing the GO/DONE bit during the last ½ TAD cycle in method 2 is a great put-off. I just don't like the idea of stopping a conversion at "just the right time." Seems too risky. So I opted for putting the MCU to sleep. Sounds simple and straightforward. Here's my version of Method 1.

unsigned int ADC(int8 ch)
  ADCON0 = ch << 2;                    // left shift ADC channel to be read from into ADCON0
  ADCON0.ADON = 1;                     // power up ADC
  ADCON1 = 0b11110000;                 // right justified, use Frc, Vdd as positive reference voltage
  INTCON.GIE = 0;                      // global interrupt disabled
  PIR1.ADIF = 0;                       // clear ADC interrupt flag
  PIE1.ADIE = 1;                       // ADC interrupt enabled
  INTCON.PEIE = 1;                     // peripheral interrupts enabled
  ADCON0.GO = 1;                       // start ADC conversion
  asm {sleep}                          // zzzzzzzzzzzzzzz.... when ADIF = 1 MCU will wake up and will continue execution of the program
  PIR1.ADIF = 0;                       // clear ADC interrupt flag
  PIE1.ADIE = 0;                       // disable ADC interrupt
  INTCON.PEIE = 0;                     // disable peripheral interrupts
  ADCON0.ADON = 0;                     // power down ADC
  return ADRESH*256 + ADRESL;          // return the 10-bit ADC value as a 16-bit number

I've been testing the circuit using the above function instead of mikroC's and so far so good. WDT is enabled and so far no resets have occurred. Will do extended testing over the week to make sure the problem has been truly licked.

[Addendum: Forgot to mention that, as per the errata sheet, the ADC bug appears only on 1822's with device revision ID#6. I did check the parts (see the errata for instructions on how to do this) I have before even attempting to write the workaround and of course as luck would have it, I had been shipped the affected components. Sigh.]

Monday, July 4, 2011

Why hasn't Microchip fabricated IAs?

I only use op amps which are single supply and I practically don't use any other op amp except those by Microchip because they're cheap, easy to obtain, and a good number of them have rail to rail I/O. Well, right now I need a single-supply instrumentation amplifier to clean up possible common mode noise from a sensor that'll be located many meters away from the control board. No amplification is necessary or desired. Unfortunately the only IA I have on hand is the INA126 which needs a bipolar supply (else output won't go all the way to ground) and has a minimum gain of 5.

Because the input signal is low impedance I could just use a differential amplifier. I can cobble one up using, say, a MCP6021, but that would entail the hassle of either getting precision resistors or manually matching four. It also increases the parts count. So that got me wondering why Microchip--given their wide range of op amps--hasn't taken the next step and developed instrumentation and difference amplifiers. Do they lack the capability to make these high precision components? Is the demand for these items too small? Is that market cornered by Analog Devices and Texas Instruments?

It's still on my wishlist and hopefully Microchip does get around to fabricating IAs in the near future. I'm sure they'd be more affordable than, say, the AD623.

Sunday, July 3, 2011

mikroC Pro v4.60 bug

Serendipitiously the mikroC Pro v5.00--successor to v4.60--was just released a couple of days ago. Because I ran into a brick wall programming the PIC12F1822 with the 4.60. The firmware compiled without a hitch for the PIC12F615 but then the compiler started going bananas when I changed the MCU to 12F1822 (had to change the MCU because it turns out I need EEPROM which unfortunately doesn't exist in the 12F615). After systematically commenting out various parts of the program to isolate what was tripping the compiler, I narrowed down the problem to a local variable that the 4.60 demanded be initialized, else it would give me a "362 Not enough RAM 'R15' P12F1822.c" error message upon compilation. Here's an example. The following will result in an error:

char  i;

void Mozza()
  char i;
  for (i=1; i<8; i++)  ;

void main()
  while(1) ;
You can change for (i=1; i<8; i++) to something else, like PORTA += i; and the compiler will cough out the same error message. On the other hand, the following versions will compile properly:

char  i;

void Mozza()
  char i=0;    // no error if variable initialized
  for (i=1; i<8; i++)  ;

void main()
  while(1) ;

char  i;

void Mozza()
  // char i;   no error if global variable is used
  for (i=1; i<8; i++)  ;

void main()
  while(1) ;
So using a global variable or initializing the local var should've been an easy workaround. Unfortunately, when mikroC's built-in EEPROM write functions are present even these tricks fail to lick the problem.

I just installed v5.00 and they've apparently fixed this bug. The firmware (still a work in progress) has thus far compiled without the error 362 message popping up.