Project - Smart Dimmer


A smart networked remote Lamp Dimmer or Motor Speed Controller:
Based on the Rotary Encoder Dimmer Project for optional local control.

The original script is based around 2 user-controllable dimmer variables:
    value - brightness value (from 0 fully Off to 100 fully bright)
    active - 0 disables output, 1 enables output  (irrespective of value)

The EasyNet version provides remote control of the same 2 variables
    target_name  Value  [value]  (from 0 fully Off to 100 fully bright)
    target_name  Active  [0]  [1]  (0 to disable output, 1 to enable)

Omitting the optional parameter will cause either instruction to reply to sender with its current value without changing anything.
Note that it is possible to set Active variable to 0 to disable the output even if Value = 0 (so is effectively Off anyway), and the output will then remain disabled even when the Value is changed to be non-zero.
Note also that changes in Value are sudden jumps, so changing from 0 to 100 and vice versa are like turning a switch on or off.

An additional instruction  Toggle  is included for equivalent hardware compatibility to toggle the output On or Off
    target_name Toggle   (no parameters)


The EasyNet instructions mentioned above were just for consistency and completeness.
What really makes this dimmer 'smart' is the addition of a DimTo instruction...
As its name implies, DimTo  [value]  changes are smoothly progressive without sudden jumps.
Also, DimTo assumes that progressive changes are meant to be seen, so automatically enables the output if it was turned off.
Normal usage is...
 target_name  DimTo  [value from 0 to 100]

This will cause a smooth progressive dim up or dim down from the previous value to the specified new value.
For example, if the current value is 80, sending "nodename  DimTo  40" will smoothly dim down from 80 to 40.
Specifying a value of 0 will dim down to fully Off, and specifying a value of 100 will dim up to fully On.
DimTo by itself without any parameter will reply to sender with the value of the variables, which remain unchanged.
So the DimTo usage is very easy and intuitive, and offers a simple means of stylish remote dimmer control.
An additional DimDelay instruction allows changing the dim up/down speed, which can be saved to file and reloaded at next startup.
This DimTo usage video should be easier to understand than words.

For added convenience DimTo can also recognise some non-essential parameters, useful for interactive control from other devices.
DimTo  On  - dims up to Value=100, ie: fully On.
DimTo
  Off  - dims down to 0, ie: fully Off
DimTo  Toggle - toggles between DimTo  On and DimTo  Off.

Basic:
'EasyNet Smart Dimmer - Electroguard - developed on Annex 1.41 beta 3"   
nodename$  = "Dimmer1"             'Assign a unique node name of choice, else it will be called "Node" + the node IP
groupname$ = "Lamps\Dimmers"       'concatenated group names are searched for a partial match
localIP$   = WORD$(IP$,1)
netIP$     = WORD$(localIP$,1,".") + "." + WORD$(localIP$,2,".") + "." + WORD$(localIP$,3,".") + "."
nodeIP$    = WORD$(localIP$,4,".")
udpport    = 5001                  'change to suit your own preference, but don't forget to do the same for all nodes
if nodename$ = "" then nodename$ = "Node" + nodeIP$
data$=""
udp.begin(udpport)
onudp RXudp
instructionslist$ = "DimTo DimDelay Value Active Toggle List Reply Report Reboot " 'List of action Subdirs
dimmer.setup 4,5,0,1               'configure dimmer module, 4 and 13 = zero crossing,  5 = pwm
dimmer.limits 0, 9500              'actual min/max delay in triac firing
value=0                            'initial startup value
active=1                           'dimmer output On=1, Off=0
dimdelay=20                        'dim up/down delay between step changes, over-ridden by dimdelay file
'if file.exists("dimdelay")=1 then dimdelay=val(file.read$("dimdelay")) 'uncomment to load a previous saved dimdelay
if active=1 then outputvalue=value else outputvalue=0
dimmer.brightness outputvalue
option.pwmfreq 100                 'reduce pwm frequency to reduce cpu load
ledpin=15                          'optional pwm led power indicator
pwm(ledpin)=outputvalue     
rotaryA=12                         'Rotary encoder pulse A output pin
rotaryB=14                         'Rotary encoder pulse B output pin
pin.mode rotaryA, input, pullup
pin.mode rotaryB, input, pullup
interrupt rotaryA, rotation        'comment out this line if not using a rotary encoder
rate=10                            'rotary encoder rate of change
debounce=50                        'debounce duration for button and rotary encoder contacts
buttonpin=0                        'optional gpio0 button to toggle output On/Off
pin.mode buttonpin, input, pullup
interrupt buttonpin, triggered     'comment out this line if not using a push button
sensor1pin=2                       'optional occupancy sensor (PIR or radar module)
pin.mode sensor1pin, input, pullup
interrupt sensor1pin, triggered
ontime=5                           'occupancy - non-retriggered On time in secs before dimming to off
oncounter=0                        'occupancy
dimofftime=10                      'occupancy - time in secs, taken to dim down to Off
dimcounter=0                       'occupancy
heartrate=1000                     'occupancy
wait

triggered:
if pin(sensor1pin)=1 then          'active high triggered
 timer0 heartrate, heartbeat                  '
 oncounter=ontime
 dimcounter=0
 data$=str$(100)
 gosub DimTo
endif
return

heartbeat:
if oncounter=1 then dimcounter=dimofftime
if oncounter >0 then oncounter=oncounter-1
if dimcounter > 0 then
 dimcounter=dimcounter-1
 data$=str$(int(100/dimofftime*dimcounter))
 gosub dimto
endif
if (oncounter=0) and (dimcounter=0) then timer0 0
return

pressed:
interrupt buttonpin, off
pause debounce
if pin(buttonpin)=0 then gosub toggle
interrupt buttonpin, pressed
return

rotation:
interrupt rotaryA, off
if pin(rotaryA)=0 then
 if pin(rotaryB)=0 then gosub moveup else gosub movedown
endif
pause debounce
interrupt rotaryA, rotation
return

moveup:
value = value + rate
if value>100 then value=100
gosub change
return

movedown:
value = value - rate
if value<0 then value=0
gosub change
return

change:
if active=1 then outputvalue=value else outputvalue=0
pwm(ledpin)=outputvalue*10
dimmer.brightness outputvalue
return

DimTo:
if data$>"" then
 select case lcase$(data$)
 case "on": d=100
 case "off": d=0
 case "toggle": if value=0 then d=100 else d=0
 case else: d=val(data$)
 end select
else
 gosub report
 return
endif
active=1        
gosub change
v=value
if d>v then for c=v to d else for c=v to d step -1
 value=c
 gosub change
 pause dimdelay
 timer0 0
next c
data$=""
timer0 heartrate, heartbeat
return

DimDelay:
if data$="" then
 udp.reply nodename$+" dimdelay="+str$(dimdelay)
else
 dimdelay=val(data$)
 file.save "dimdelay",str$(dimdelay)
endif
return

Value:
if data$="" then
udp.reply nodename$+" value="+str$(value)
else
 value=val(data$)
 gosub change
endif
data$=""
return

Active:
if data$>"" then
 active=val(data$)
 gosub change
else
 udp.reply nodename$+" active="+str$(active)
endif
data$=""
return

Toggle:
active=1-active
gosub change
return

LIST:
udp.reply Nodename$ + " instructions="+instructionslist$
return

REPLY:
udp.reply "Reply from " + Nodename$
return

REPORT:
if active=1 then msg$="On" else msg$="Off"
udp.reply Nodename$ + " Output="+msg$+", Brightness="+str$(value)+", DimDelay="+str$(dimdelay)
return

REBOOT:
reboot
return

RXudp:
RXmsg$ = udp.read$
target$ = ucase$(word$(RXmsg$,1))                  'Target may be NodeName or GroupName or "ALL" or localIP address
if (target$=localIP$) OR (target$=ucase$(nodename$)) OR (instr(ucase$(groupname$),target$)>0) OR (target$="ALL") then
 instruction$ = trim$(ucase$(word$(RXmsg$,2)))     'Instruction is second word of message
 data$ = "": getdata data$,RXmsg$," ",2            'extract any data that follows the instruction
 if word.find(ucase$(instructionslist$),instruction$) > 0 then
  gosub instruction$                               'branch to action the corresponding instruction subroutine
 else
  udp.reply RXmsg$ + " INSTRUCTION NOT RECOGNISED"
 endif  'word.find
endif   '(target$=localIP$)
return

sub GetData(ret$,v$,sep$,pos) 
local i,p,q
p=1
for i=1 to pos
 p=instr(p+1,v$,sep$)
 if p>0 then p=p+len(sep$)
next i
if p=0 then
 ret$=""
else
 q=instr(p+1,v$,sep$)
 if q=0 then q=999
 ret$=mid$(v$,p)
endif
end sub

sub WordParse(ret$,full$,search$,sep$)  'extracts value from option=value (thanks cicciocb)
local p,b$
p=instr(full$,search$)
if p<>0 then
 b$=mid$(full$,p+len(search$))
 ret$=word$(b$,1,sep$)
else
 ret$=""
endif
end sub

end '-------------------- End --------------------




Smart Occupancy Controller
As an experiment I added facility for a motion sensor such as a PIR or radar module to turn the dimmer into an occupancy controller, eg: for automatically controlling a bathroom light and/or fan. It worked well during testing (see this demo occupancy video), although I would probably disable the rotary controller by commenting out "interrupt rotaryA, rotation if planning to use it in earnest (because the occupancy and rotary encoder are both based on interrupts and timings, which would be asking for trouble)
If you want to give the occupancy dimmer a try, just un-comment "pin.mode sensor1pin, input, pullup"  and add a sensor to gpio2.
The lamp or extractor fan is smoothly turned up if movement is detected, and stays on 'full' while being re-triggered, plus for a preset 'ontime', before slowly dimming down to off during a preset 'dimofftime, unless re-triggered.


This script follows the normal EasyNet syntax of
target_name  instruction  [optional parameters]

where  target_name can be either the unique nodename OR its unique IP_address OR a partial group_name OR  "All"
target_name and instruction are not case sensitive, so Instruction = INSTRUCTION = iNsTrUcTiOn  etc.

The idea behind EasyNet is to offer simple networked interaction of devices for non-experts, so here is a video snippet showing the different ways that an EasyNet node could be addressed.

And be aware that the use of the Toolkit UDP Console in the video is to demonstrate the use of the dimmers EasyNet instructions, whereas in practice the  EasyNet instructions would be issued interactively from other EasyNet devices... some of which might be your own tailored Annex web page control console from phone or tablet or computer. The browser only need connect to one Annex device Output page in order to interact with many UDP devices on the network, forming your own unique automation and control system.

Comentarios