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.
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 Toggle - toggles between DimTo On and DimTo Off.DimTo Off - dims down to 0, ie: fully 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. 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.
|