We have a system where we can reliably count the rotary switch detents and determine the direction however with a 24 detent per revolution switch and 4096 possible output levels, we need to add some sort of scaling since as-is it would take some 170 full rotations of the switch to go from full-off to full-on – hardly user-friendly!
Other factors come into play too – whilst the LED’s have a reasonable linear current to Lux (Illuminance) output the human eye is non linear -at low levels of illumination it is very sensitive to illumination changes whereas at high levels of illumination it is very insensitive to quite large measured changes in illumination.
In reality this affects the human <-> switch interface since at high levels of illumination a linear mapping of switch to PWM width a single click will produce an almost imperceptibly small increase in brightness – the end-user may think that a single click has made no change because they perceive no visual change in state, whilst at low levels it will exhibit steps that are far too large for the user to exercise fine control.
To address this we have to use a correction factor and like a lot of natural science behaviours it is logarithmic, so what we need is something like this –
This allows more ( to the human eye) even step changes at low and high illumination values and is similar to one of the industry standards DALI (IEC 62386) specifications regarding digital to illumination levels, although overall the system described here makes no attempt to conform to that standard.
I have calculated values for this at integer values from 0-100 so ~4 full rotations of a control switch would go from ‘off’ to full- brightness, however in practice given rounding of decimal places, as the PWM chip only accepts integer values we end up with only ~80 steps which in practice is still an acceptable figure.
Another aspect is that below a particular voltage threshold the LED’s simply do not turn on – likely a physics quantum effect, and in practice a bit like conventional fluorescent lamps as you increase the voltage upwards from zero, there comes a point when they will suddenly illuminate albeit at a low visual level and at that point it is possible to back off the current and achieve a lower ‘on’ level. This is due to hysteresis in that once the phosphor coating is excited and emitting visible light one can lower the input ‘charge’ energy and some phosphors will still be excited and glow, however in my use-case this level is so low for an uplighter as to be irrelevant in practice.
So the takeaway from this is twofold:
- We need to have an appropriate scaling factor so that the input switch rotational detents produce perceptible light level changes throughout the full brightness range
- We need some ‘per-led-type’ minimum – and likely maximum value too that is configurable for each different type of LED fixture deployed such that minimum ‘on’ level equates to one detent above their true ‘off’ level and also that the devices maximum current levels are not exceeded.
To solve (1) we can use a lookup table – a list would suffice in Python since it is so small – ~80 values, and to solve (2) we could have an initialisation file that contains a per-light record of min/max values and which is read in at startup into a list that can be dynamically referenced as required.
Using a weighted scaling curve and measuring actual lux levels from a led and plotting these against LED current gives the following graph – it is reasonably linear and from a practical perspective it works in that detents on the switch rotation provide noticeable but acceptable gradations in perceived illumination
Note: I filtered the light using a sheet of paper between the LED and the light meter since the full power output of the LED @ ~2000 lux would have driven my light meter off scale! –
Here is the code implementing the logarithmic scaling to the switch detent count –
For the initialisation file I used a simple ‘human readable’ form consisting of lines –
The ‘on’ value being the setting when the push button action of the PEC16 switches I’m using are activated, and the min/max values representing the minimum current to just turn the respective LED ‘on’, and max the maximum ‘on’ current the LED fixture can take.
This allows flexibility in that for example my kitchen and bathrooms where I have IP rated ceiling mounted down-lighters which have different electrical/optical characteristics from the uplighters I can individually control the default initial brightness level, the maximum level the LED can accommodate, and also the default brightness level when switched on via the push-button of the rotary switch.
I have not detailed the implementation of the push-button switch code – it is a simplified sub-set of the rotary decoder code and since the hardware is cheap I have dedicated individual MCP23S17 chips purely for managing the push-button functions since this made the switch enumeration simpler, including a timed double press routine to allow interactive setting of each individual light’s default ‘on’ level -i.e. you set the light to the level you want it turned on to- i.e. the minimum, then double press the button and that will save the setting so that subsequent button presses will turn the light off or on to the levels you have prescribed.
Complete code for this additional functionality is provided at the end of this blog.
Below is the what the prototype looks like, it is to be honest a bit of a lash-up and is complicated due to the probe wiring necessary to instrument the SPi and I2C buses, which obviously would not be necessary for the finished project!
Since I need to implement 24 rotary encoders and 32 switches -I needed extra switches to handle garden lights, IoT sensor controlled lights etc, I decided to re-write the core code into a class and pass as arguments the Interrupt pin No., the switch start number and the instance ID –
Now I can launch any number of them like this –
So now let’s address the initialisation aspect so that we can cater for different LED types with different minimum ‘on’ and different maximum current values..