A description of how to set up a switchable film on your window (that is, an electric window blinder) with wireless automatic control based on movement sensor and time of the day.
I live in one of those apartments where the exit door points directly to an outdoor shared balcony, where neighbors often pass by on their way from and to their homes. What complicates the situation a little bit is that the only existent window in that part of the apartment is right next to the exit door and I can’t just leave it always closed with blinds or curtains as it will make 80% of the apartment permanently dark. It’s more like a privacy concern given that I often seat at the couch near that window and people passing by instinctively stare inside even for a moment. That’s why I started thinking about a solution that would close the window just for a moment when someone passes by and allows the sunlight come inside all the remaining time.
Switchable film is a relatively thick self-adhesive film (~0.5 mm / 0.02 inch) which is delivered pre-cut to your window dimensions and which you simply attach to the window from the inside. There are few of them at the market, but general idea is the same. The film is delivered in a huge heavy wooden frame (mine was around 80 kg / 176 lbs), being the size of the window, because film can’t handle bending or folding, it must be handled very carefully with special gloves included in the package.
The technology is described in more details on Wikipedia, but generally it is liquid crystals inside a film that make it transparent when the power is on, and opaque when the power is off. There is also a possibility to order a switchable glass, which is literally a glass, not just a film – but that was just an overkill for my purposes.
I would like to thank my landlord who paid for the film.
There is a metal stripe to wire the film precisely at the top of it, so it’s reasonably convenient to hide the wires in order not to spoil overall room interior. If you wish just to insert it to a power supply – that’s it, film should work. You switch the power on – it becomes transparent, you switch the power off – it becomes opaque. It’s opaqueness is good, people can’t see inside (neither can you outside). Regarding transparency – you should be prepared that it’s not 100% transparent, it looks a bit milky, but only if you stare at it close enough:
To achieve initial goal – make the window opaque when people are passing by – we need a motion sensor. Requirements there were: tolerance to outdoor conditions (especially winter conditions in Sweden) and wireless connection in order to skip drilling the wall to outside. I chose a cheap Nexa motion sensor which meets all above criteria plus transmits signals on quite popular 433,92 MHz frequency which is handy as it’s easy to find a cheap transmitter for this frequency. I’ll describe why it’s useful later on.
On the receiver end it’s the easiest to buy a compatible Nexa power socket with a remote control. So it sounds like everything is done: we have a motion sensor outside, power socket inside and we just insert power cord from the switchable film to the power socket. Is that all? No, read on 🙂
Inversion of power control for the film
Motion sensors of that type are normally designed to switch the light in your garage on when you enter and switch it off automatically after a certain interval (it’s configurable, I use about 15 seconds). And it works totally fine: it sends a signal and the power socket gives a power to a connected device.
But, as you remember, having a power for the film means it becomes transparent which is exactly the opposite of our goal. So we have to invert the power supply, making it disconnected when the motion sensor sends a signal and connected all remaining time. Thanks to my colleague Piotr and electric components generously donated by him, the solution for that was as described on the scheme:
Given everything connected, the initial goal was achieved – no more staring neighbors.
Nexa provides a remote control for their power sockets, so it’s possible to switch on/off the window manually when needed. But the next thing I wanted to implement – automatic opaqueness for night time (especially given that tech specs for the film advise to power it on no longer than 16 hours per day). In Sweden daylight duration changes from around 4h in winter to about 21h in summer. Of course, it should be some automatic solution to calculate a sunrise and sunset for a current date.
Luckily for me, a protocol being used to transmit commands from the motion sensor to power socket has been successfully decoded and described in that blog.
Again, to avoid wires all over the room, the easiest for me was to use my Raspberry Pi and attach to it a 433,92 Mhz transmitter in order to control power sockets on demand. That allows to implement logic of any kind of complexity.
The project started as a prototype in Python (code is available on Github) and it might be worth rewriting it in C++ for better encoding stability (because timings between transmissions must be relatively precise), but for now it works at acceptable level.
This python project allows to control the window based on time of the day (switching on at sunrise and switching of at sunset) and based on the presence of me or my spouse at home.
Time control is trivial and presence control works by interrogating my home router to see whether or not predefined MAC addresses of our mobile phones are in it’s Wi-Fi coverage. This gives us a pleasure to leave the window on it’s own. It switches off when we’re out and switches on when we’re back. Same automation for day/night switches.
Encoding Nexa protocol in Python
def sleep_T(self, T_num): time.sleep(T_num*250/1000000.0) def send_physical_bit(self, bit_value): if bit_value: GPIO.output(self._data_pin, True) self.sleep_T(1) GPIO.output(self._data_pin, False) self.sleep_T(1) else: GPIO.output(self._data_pin, True) self.sleep_T(1) GPIO.output(self._data_pin, False) self.sleep_T(5) def send_bit(self, bit_value): self.send_physical_bit(bit_value) self.send_physical_bit(not bit_value) def send_sync(self): GPIO.output(self._data_pin, True) self.sleep_T(1) GPIO.output(self._data_pin, False) self.sleep_T(10) def send_pause(self): GPIO.output(self._data_pin, True) self.sleep_T(1) GPIO.output(self._data_pin, False) self.sleep_T(40) def send_on_off(self, on_off): self.send_sync() #transmitter code binary_number_string = format(self._transmitter_code, '08b') for digit in binary_number_string: bit = digit == '1' self.send_bit(bit) #group code self.send_bit(True) #on/off bit, on = 0, off = 1 self.send_bit(not on_off) #Channel bits. Proove/Anslut = 00, Nexa = 11. self.send_bit(True) self.send_bit(True) # Unit bits. Device to be turned on or off. # Nexa Unit #1 = 11, #2 = 10, #3 = 01. self.send_bit(True) self.send_bit(True) self.send_pause() def switch(self, on_off): GPIO.setmode(GPIO.BOARD) GPIO.setup(self._data_pin, GPIO.OUT) for x in range(0, 5): self.send_on_off(on_off) # Send the signal one more time, # sometimes it happens not to be decoded correctly time.sleep(1) for x in range(0, 5): self.send_on_off(on_off) GPIO.output(self._data_pin, False) # Make sure that we do not leave PIN in 'on' state GPIO.cleanup()
Key things here are the ability to sleep exactly 250 μs and to send zeros and ones by giving power to a GPIO pin for this time interval. As I mentioned previously, it’s not always easy to reach this level of time sleep precision for python code, so I simply send the command 5 times so that at least some of them are decoded on the power socket.
Monitoring controller uptime and window status
Raspberry Pi and all used hardware is far from enterprise level stability, so I wanted to have some kind of external monitoring, so I could receive automatic alerts about occasional system downtime. Free plan of Datadog worked just well for that goal. Python app on Raspberry Pi periodically sends statistics about current window status to datadog servers and if there is a gap in those pings, datadog simply e-mails me. Also I get these graphs when window was transparent (1) and opaque (0):
Monitoring people passing by
That might be a little more a surveillance topic, but I was just wondering about other possible usages of this wireless installation. In Sweden the law prohibits you from installing your own surveillance camera, you have to get a permission for that, and this is not a simple process. But nobody forbids to install a motion sensor. Information we can get from it – number of times it triggers per hour for example. So for a start I was thinking about some kind of online graph with people per hour rate.
I happened to have a DVB-T USB stick which allows you to listen on a wide range of frequencies. So I connected it to Raspberry Pi and forked ook-decoder project which supports sampling of general On-Off Keying protocols in C language (which essentially Nexa protocol is).
I added Nexa protocol decoding and datadog statistics upload support (using StatsD protocol). Key thing here is that each Nexa transmitter has a unique ID, which you have to decode first, so that you can filter out only its messages later.
Turned out that this part was more for fun than for real practical usage. I get information on graphs about some people passing by at 3am sometimes but this information is pretty useless 🙂
Apple Watch app
Despite the fact that Nexa power sockets come with remote control units, it sometimes is too demanding to reach out for it 🙂 So I decided to write a very basic Apple Watch app with 2 buttons for opening and closing the window:
Stability and results
Everything that was planned has been successfully implemented, though overall stability of the system might be improved. Sometimes (about once per month) the motion sensor gets stuck in a ‘closed window’ state, and the only solution is to put a hand in front of it for a second. This seems like a hardware issue and possible resolution would be to buy a better motion sensor – not worthwhile for me at the moment.
Also despite the python controller repeats signal transmission 5 times in a row, it sometimes is still not received correctly (maybe twice per month), so the system stays in a ‘detached head’ state, e.g. the controller thinks that the window is closed, but in fact it is opened. A bit annoying when it is left opened all day long because of this bug. I suspect it’s related to insufficient sleep precision in Raspberry Pi. I tried to start the process with real-time priority but it makes almost no difference. Perhaps it’s worth trying to rewrite the sender in C++, but I haven’t justified the work for myself yet, given that this bug happens relatively rare.
Overall, I’m quite happy with the project. It works successfully for more than a year already, and I would definitely recommend it to anyone who has the same privacy concerns like I did.
P.S. Cats like it 🙂