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 outputvalueoption.pwmfreq 100 'reduce pwm frequency to reduce cpu loadledpin=15 'optional pwm led power indicatorpwm(ledpin)=outputvalue rotaryA=12 'Rotary encoder pulse A output pinrotaryB=14 'Rotary encoder pulse B output pinpin.mode rotaryA, input, pulluppin.mode rotaryB, input, pullupinterrupt rotaryA, rotation 'comment out this line if not using a rotary encoderrate=10 'rotary encoder rate of changedebounce=50 'debounce duration for button and rotary encoder contactsbuttonpin=0 'optional gpio0 button to toggle output On/Offpin.mode buttonpin, input, pullupinterrupt buttonpin, triggered 'comment out this line if not using a push buttonsensor1pin=2 'optional occupancy sensor (PIR or radar module)pin.mode sensor1pin, input, pullupinterrupt sensor1pin, triggeredontime=5 'occupancy - non-retriggered On time in secs before dimming to offoncounter=0 'occupancydimofftime=10 'occupancy - time in secs, taken to dim down to Off dimcounter=0 'occupancyheartrate=1000 'occupancywaittriggered:if pin(sensor1pin)=1 then 'active high triggered timer0 heartrate, heartbeat ' oncounter=ontime dimcounter=0 data$=str$(100) gosub DimToendifreturnheartbeat:if oncounter=1 then dimcounter=dimofftimeif oncounter >0 then oncounter=oncounter-1 if dimcounter > 0 then dimcounter=dimcounter-1 data$=str$(int(100/dimofftime*dimcounter)) gosub dimtoendifif (oncounter=0) and (dimcounter=0) then timer0 0returnpressed:interrupt buttonpin, offpause debounceif pin(buttonpin)=0 then gosub toggleinterrupt buttonpin, pressedreturnrotation:interrupt rotaryA, offif pin(rotaryA)=0 then if pin(rotaryB)=0 then gosub moveup else gosub movedownendifpause debounceinterrupt rotaryA, rotationreturnmoveup:value = value + rateif value>100 then value=100gosub changereturnmovedown:value = value - rateif value<0 then value=0gosub changereturnchange:if active=1 then outputvalue=value else outputvalue=0pwm(ledpin)=outputvalue*10dimmer.brightness outputvaluereturnDimTo: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 selectelse gosub report returnendifactive=1 gosub changev=valueif d>v then for c=v to d else for c=v to d step -1 value=c gosub change pause dimdelay timer0 0next cdata$=""timer0 heartrate, heartbeatreturnDimDelay:if data$="" then udp.reply nodename$+" dimdelay="+str$(dimdelay)else dimdelay=val(data$) file.save "dimdelay",str$(dimdelay)endifreturnValue:if data$="" thenudp.reply nodename$+" value="+str$(value)else value=val(data$) gosub changeendif data$=""returnActive:if data$>"" then active=val(data$) gosub changeelse udp.reply nodename$+" active="+str$(active)endifdata$=""returnToggle:active=1-activegosub changereturnLIST:udp.reply Nodename$ + " instructions="+instructionslist$returnREPLY:udp.reply "Reply from " + Nodename$ returnREPORT:if active=1 then msg$="On" else msg$="Off"udp.reply Nodename$ + " Output="+msg$+", Brightness="+str$(value)+", DimDelay="+str$(dimdelay)returnREBOOT:rebootreturnRXudp:RXmsg$ = udp.read$target$ = ucase$(word$(RXmsg$,1)) 'Target may be NodeName or GroupName or "ALL" or localIP addressif (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.findendif '(target$=localIP$)returnsub GetData(ret$,v$,sep$,pos) local i,p,qp=1for i=1 to pos p=instr(p+1,v$,sep$) if p>0 then p=p+len(sep$)next iif p=0 then ret$=""else q=instr(p+1,v$,sep$) if q=0 then q=999 ret$=mid$(v$,p)endifend subsub 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$=""endifend subend '-------------------- 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.