====== Wall Timer ====== After reaching my limit with my unreliable D-Link 604+ ADSL router, I decided that manually power-cycling the thing was becoming a chore, and more importantly when I am asleep or my room is otherwise off limits to my housemates a crash means no connectivity for them, and no way to overcome it! I'd been thinking of modding some junk I had to help me for a while, but it was only when I was prompted by an irate housemate that I took up the challenge. ===== Junk transformed ===== {{walltimer.jpg }} I have an unused digital wall timer among my junk, it hasn't worked for some time now --- I suspect the inbuilt rechargable battery has died or similar because the LCD never displays anything and the ''ON/OFF'' button doesn't work; which was typical of when I first used it and had to wait for the inner battery to charge before it's first use. You can see from this picture ( which is the end product of my efforts ) that the LCD is still not working, but seems healthier than before. Who knows, perhaps it will magically start working? I opened it up, and would you believe it was very nice and neat inside? Lots of ordinary components that any determined geek could understand. After turning the thing over in my hands a few times and giving it a thorough examining it was clear that all I needed to do was solder my own wires onto the 3 existing wires inside, which connected the relay system to the LCD/Control panel. At first I soldered my wires to Vdd and CTL (the nice people of ''micromark'' took the trouble of printing meaningful labels on the PCB ^_^). When I shorted the wired (and closed the circuit) the relay fired and powered it's socket, when I seperated the wires (and hence broke the circuit) the relay returned to it's ease and stopped powering the socket. So far, so good. I used a pair of wide pliers and firmly gripped the end of the plastic chasis, and tore off a neat rectangular section which I fed my wired through. Took a little rocking back an forth to make sure I got a nice clean slice. :-) ===== Getting it hooked up ===== Once I had a method of turning the mains on and off, I needed to interface this with my PC with a view to code a program which checks for internet connectivity and then power-cycles the router as needed. First first thought was to use one of my PC's serial (RS232) ports to do this, as I have got some experience using them from my [[project|final year project]]. I did some [[http://www.camiresearch.com/Data_Com_Basics/RS232_standard.html|research]] and decided that the "DTE Ready" (pin 4) would be a good candidate, as all that would be required to temporarily change this pin's signal would be to 'open' the appropriate COM port. I tested this using HyperTerminal - connecting to the COM port and releasing it did indeed change the signal. This would have been ideal, except that RS232 is +/- 12V, and that's not really very useful for closing/breaking a circuit. I probably would have tried using a diode of some sort if I had one handy, but I didn't. {{ parallelport.jpg}} In fact it was at this point that I realised there was something fundamentally wrong with my approach - I was trying to use the PC as a switch, which is not really in keeping with the TTL way of doing things. But being lazy I didn't want to resolder the wall timer, instead I tried using the parallel port. I uncovered a sturdy source of info on [[google>programming parallel ports]], and my favourite of the lot is definately [[http://www.epanorama.net/circuits/parallel_output.html|this one]]. It was there that I came across the excellent [[http://www.kemo-electronic.com/en/module/m125/|Kemo M125 kit]] and, more importantly, it's raw parallel port controlling GUI for WinXP (getting access to the parallel port in WinXP has been something of a bother to me in recent times, but I digress...) I had some trouble finding which pin was 'pin 1' on my parallel port (as none of the pages seem to feel this is an important detail). I found that for my own setup it was the bottom pin on the longer of the two columns((this is looking directly at the rear-end of my PC, with the motherboard mounted on the left of the case)). I had some limited success with this approach, connecting my wired to data pin 1 (which is physical pin 2) and one of the grounds (pins 18-25) did indeed get the relay to turn on and off, but it made an awful buzzing noise when it was on, and a not-so-awful-but-still-audible buzzing when in the off state. Not that the noise was what bothered me, it's the fact that I have created something which is doing seriously dangerous things with mains power! I summoned some effort, overcame my laziness and re-soldered my bastard of science. This time I got it right: I soldered to Vss and CTL. I then connected it up to my parallel port in the same manner, but had no success, so I reversed the wires and //then// it worked. I labeled my wires; the yellow tagged one is the drive, the other is the ground. :!: I rebooted my machine to see what happens to the pins, and they remain on until the BIOS POST screen where they turn off, then when the WinXP logo fades in they pop on again, and after some delay they flicker a bit and stay on (I'm now fully booted and they're still on). I presume that Windows remembered how the port was set when it shut down, and restored those settings. ===== Writing and compiling my own control code ===== It took a while to find a library I was happy with, and eventually I settled on [[http://www.internals.com/|WinIO]], which contains a threesome of ''.dll'', ''.vxd'' and ''.sys'' for making it all happen. What made me settle on it was it's inclusion of a help file and two sample projects, one for MS Visual Basic and the other for MS Visual C++ --- complete with built versions of the projects, which meant I could verify that they worked on my setup. After much a-do trying to get MS Visual Studio .NET to install properly,((My offical 2005 beta version had problems reading the disc :-/, so I went back to a less scrupulous copy I aquired from a nice man in Hong Kong.)) I soon emerged victorious after adding the ''WinIO.lib'' file to my project and the modifying the example code to: **Note:** This is actually the second version of the code. // PowerCycler.cpp : Defines the entry point for the console application. // // Makes use of WinIO library to gain access to the parallel port, ensure // that WinIo.dll, WinIo.sys and WINIO.VXD are in the same directory as // the final executable, or in the environment path #include "stdafx.h" #include "winio.h" #include #include #include #define MAX_ERRORS 3 // Function prototypes int ConnectivityController(); int ManualOverride(char*); int _tmain(int argc, _TCHAR* argv[]) { switch(argc) { case 1: ConnectivityController(); break; case 2: ManualOverride(argv[1]); break; default: printf("\n PowerCycler v1.1\n Robert Meerman (robert.meerman@gmail.com)\n USAGE: PowerCycler [ON | OFF | TOGGLE]\n"); printf("\n\tIf called without arguments, http://192.168.0.1/ will be polled at 5 second intervals for connectivty.\n"); printf("\tUpon 3 failures to connect data pin 1 (physical pin 2) of LPT1 is set low for 5 seconds, and then brought back\n"); printf("\tand polling resumes after 30seconds.\n"); printf("\n\tWhen run with a single argument data pin 1 of LPT1 is set accordingly. Valid arguments are: ON | OFF | TOGGLE\n"); } } int ManualOverride(char* command) { if(InitializeWinIo()) // Init WinIo.dll { int bits; if(strcmp(command, "on")==0 || strcmp(command, "ON")==0) { printf("On? Yessir!\n"); bits = 1; } else if(strcmp(command, "off")==0 || strcmp(command, "OFF")==0) { printf("Off? Right away!\n"); bits = 0; } else if(strcmp(command, "toggle")==0 || strcmp(command, "TOGGLE")==0) { printf("Toggle? Let's see...\n"); bits = (_inp(0x378) + 1) % 2; } else { printf("Erm, I don't know what will make you happy when you command me to %s.\n", command); } _outp(0x378, bits); // Set the parallel port (LPT1 = 0x378) ShutdownWinIo(); // DeInit WinIo.dll return 0; }else{ printf("Error during initialization of WinIo."); OutputDebugString("Error during initialization of WinIo."); return 1; } } int ConnectivityController() { OutputDebugString("PowerCycler started."); int errors = 0; while(1) { CAtlHttpClient client; printf("Sending request to http://192.168.0.1/ ..."); if (client.Navigate( "http://192.168.0.1/" )) { errors = 0; printf("OK\n"); printf("Sleeping for 5 seconds...\n"); Sleep(5000); } else { errors++; OutputDebugString("No response from router."); printf("Failed\n"); if(errors >= MAX_ERRORS) { printf("PowerCycling with 5 sec off state...\n"); OutputDebugString("PowerCycling with 5 sec off state..."); if(InitializeWinIo()) // Init WinIo.dll { _outp(0x378, 0); // Power off the router (0x378 is the memory address of LPT1) Sleep(5000); _outp(0x378, 1); // Power on the router (Router is connected to data pin 1) ShutdownWinIo(); // DeInit WinIo.dll printf("Waiting 30 seconds for router to come up...\n"); OutputDebugString("Waiting 30 seconds for router to come up..."); Sleep(30000); }else{ printf("Error during initialization of WinIo."); OutputDebugString("Error during initialization of WinIo."); return 1; } } } } } This produces an 80k executable file. The important output is also sent to a debugger, so if you don't have a debugger or tool such as [[http://www.sysinternals.com/ntw2k/freeware/debugview.shtml|debug view]] for viewing this, you will see nothing. Why? Because I wanted to run it as a headless (non-interactive) service. Non-interactive services don't have console to output to. ===== Running it as a Service ===== Now I've got it all working, the thing is powered off when it loses connectivity and is given 30 seconds to come back up before checking. So what's left to be done? Well, I do //not// want to have the console open all the time, ideally I'd want to run this thing as a windows service! Guess what? It is possible. I probably should have modified my source code and made a special build for this, but I have no idea how and by this point I was already fed up with wrestling MS Studio .NET :-( Luckily I came across a set of windows NT 4 utilities from one of the resource packs ---''INSTSRV.EXE'' and ''SRVANY.EXE''--- when I read [[http://www.taltech.com/support/sw_tricks/NTService.htm|this page]]. As I always do, I had some problems getting it to work at first, in the end the permissions for my executable were wrong - I needed to add "Full Control" permissions for ''SYSTEM'' and ''Administrators'' and that seemed to fix it. ^_^ So all that remains now is to leave it running and see how well it copes. I'm a little concerned what it will do when I intentionally reboot the router (like when I change it's configuration). I'll do some testing on it soon enough. ===== Epilogue ===== It works very nicely, and have power cycled the router 3 times since I put it in place. All three times it did this before I had noticed I'd lost connectivity, but a quick check of my upstream chart (I have it as part of my desktop, using [[http://www.ipi.fi/~rainy/index.php?pn=projects&project=rainmeter|rainmeter]]) confirmed that the router had indeed stopped responding about 30-40 seconds prior. ^_^ :!: Annoyingly, I've come across the root of my router problems((The "Proxy DNS" feature, which caches DNS entries to save bandwidth, was causing the problem, [[http://www.dlink.com.au/tech/drivers/files/routers/DSL-xxx_Disabling_Proxy_DNS.pdf|disabling this feature]] (and updating the DHCP settings to use another DNS server) solves this.)) and have done the appropriate fix. So long as my information is accurate (and it seems offical from D-Link) then this renders my lovely new system obsolete. :-( Still, it was fun to make. ;-) **Follow-up:** The router's been up for 44-days without a single hitch, certainly seems fixed. I've long ago (at least 44-days if you think about) plugged the router directly into the wall and am now using my creation to control my rope-lights on the floor, allowing me to use my infrared remote control (via my TV-card and [[http://www.mediatexx.com/|uICE]]) to power them on and off from bed. I've even got it scheduled in control panel to turn them on at about 6:30pm when it starts to get dark. ^_^