Arduino serial watchdog
Thursday, June 27 2013
I woke up early this morning and began a series of experiments to test out a solution to a problem I have been having. The solar controller in the basement, the homebrewed device that decides when it's time to run the circulation pumps, is, as you'll remember, based on Atmega328 microcontrollers running in the Arduino environment. It's connected to Woodchuck (my main computer) via a 100 foot long serial cable, which allows me to monitor its behaviour and rewrite its firmware whenever I feel like it. But there's a problem with this system, and it has to do with the way Arduino (at least of the generation I am using) reprograms its microcontrollers. When the IDE wants to reprogram the Atmega328, it asserts the DTR line of the serial cable, which forces the Atmega328 to reboot so that its bootloader program can slurp in and install the new firmware. The reason it does this is because the DTR line is connected to the Atmega328's reset line via a small (0.1μF) capacitor. While this works great in conventional Arduino environments, it's problematic when using a 100 foot serial cable. I've found that if the serial cable should become unplugged in the laboratory, that 100 feet of cable behaves like an antenna, pulling noise into the solar controller and causing the serial lines (most problematically, the DTR line) to wildly fluctuate through various states. This causes the solar controller to reboot itself endlessly, never staying up long enough to turn on any pumps. With some USB-to-serial adapters, the serial cable doesn't even have to be detached for these things to happen; they can even happen when Woodchuck is connected but hibernating. You can see how this might be a problem if I put Woodchuck to sleep and go on vacation for two weeks. What if the solar controller reboots itself endlessly that whole time? Not only will solar energy go wasted, but the hydronic fluid will boil out of the system and, though I have systems in place to recapture it, repriming the pipes is always an unpleasant job.
A possible solution to this problem would be to install a switch allowing me to disconnect the capacitor connecting the DTR line to the Atmega328's reset line. But then I'd have to go down to the basement and flip the switch on whenever I wanted to reflash the firmware. And then I'd have to remember to flip the switch off before going on vacation (or even just going to bed).
A better solution would be to have some sort of triggerable one-shot that could temporarily flip on an electronic switch connecting the DTR line to the capacitor going to the Atmega328 reset line. But how would I trigger that one shot? One possible way would be to transmit a certain sequence of characters down the serial line and have some microcontroller dedicated to looking for it. When it sees that sequence, it fires a one-shot that closes a switch connecting the DTR to the reset line via the capacitor, which leaves them connected for a period of time before disconnecting them. As an added bonus, nearly all microcontrollers are flexible enough to serve as both a one-shot and a serial line monitoring agent.
Before I could install any of this stuff, though, I had to do the experiments to demonstrate whether or not each element of system would be successful.
The obvious choice for a microcontroller capable of monitoring the serial line and then flipping an electronic switch was an ATtiny85; I'd ordered a bunch of them from Tayda electronics, where they'd cost $1.15 each. My Arduino environment is set up to allow me to program them directly from the IDE, and I have a little USB-based programming dongle. I also have a flexible homebrew environment for testing 28 pin Atmega microcontrollers, which has allowed me to work out the bugs in other solar controller systems. I could put the AtTiny-programming dongle next to the 28 pin programming system, wire them together, and see if there were any problems to be avoided.
It took no time at all to write the code to make an AtTiny behave as a programmable one-shot triggerable by a sequence of characters on a serial line. I ended up writing the code so that it would respond to serial character sequences of the form "!X[digit]" -- which would make the one-shot stay high for 10 raised to the power of [digit] seconds. Thus a sequence of "!X4" would cause the one-shot to stay high for ten thousand seconds. While I was at it, I also made the AtTiny be on the lookout for the sequence "!R" — if it saw that, it would pulse one of its few other lines for a hundredth of a second. This would allow me to hook that line to the Atmega328s' reset lines and perform a hardware reset, which is lowest level of reset there is (and impossible for the processor to mask). The AtTiny could thus serve as an external watchdog to independently control the environment of the Atmega328s. That's a lot of functionality for an eight-pin IC; given the cheapness and flexibility, it's hard to imagine ever using a 555 timer again.
Now I had to run some experiments to see if an RS-232 cable really is shareable by two microcontrollers. Initially it seemed that the answer was no, since I could not read any data from an Atmega328 when it was sharing both transmit and receive with an AtTiny. But if I removed just the AtTiny's transmit line from the Atmega328's transmit line, the Atmega328 behaved normally and could even have new software bootloaded into it. The AtTiny just sat there, listening (along with the Atmega328) to the RS-232 receive line, waiting for one of the two sequences it was supposed to act on. And when it heard one, it would either fire one of its lines as a variably-timed one-shot, or send a pulse down the other. Interestingly, whenever it received a message that was meaningful to it, the data coming from the Atmega328 from then on would be nothing but blank lines in the serial monitor. But that hardly mattered, because the serial display became good again the moment the Atmega328 rebooted, and there was no reason to tell the AtTiny to do anything unless an Atmega328 boot was imminent.
Now I had to run some tests to see how easy it would be to connect a capacitor between DTR and reset using a digital switch. The digital switch I wanted to use was one of the two bilateral switches not being used in a 4016 quad bilateral switch already present on-board. Research told me that the resistance through one of these bilateral switches when in the "on" state was about 300 ohms. Was that too much resistance? To test, I connected the capacitor from the DTR line to the Atmega328's reset line through a 300 ohm resistor and checked to see whether it was possible to bootload new data into the microcontroller from the Arduino IDE. It worked, suggesting that my entire system would be feasible.
So I brought the Solar Controller up from the basement, removed its hand-wired motherboard, installed a socket for the AtTiny85, and then wired it to the 4016, the Atmega328s' reset lines (both master and slave), and to the capacitor connected to the serial cable's DTR line. The whole system seemed to work correctly the first time I tested it.
serial watchdog schematic:
serial watchdog source code:
For linking purposes this article's URL is:feedback
previous | next