Previously we talked about monitoring the portable air conditioning unit keeping the server room cool. The SMS gateway, basically an overgrown Raspberry Pi, uses a USB decibel meter to report ambient noise volume in the room, from which we can extrapolate the state of the air conditioner's compressor and fan. The Sawtooth Power Mac G4 file server (and radio station) logs the temperature and humidity with a USB sensor of its own. The portable A/C unit exhausts warm air through a duct to the roof so the room can remain secure, and a turbine spins the hot air away into the atmosphere.
Unfortunately, what all this monitoring showed was the A/C unit was crapping out. The house central A/C's compressor died abruptly this summer after over two decades, naturally during the hottest part of the year, and it took nearly a week and $14,000 to get it dealt with (it was an old R-22 system and thus everything had to be replaced). The portable A/C was itself almost 11 years old by this time, and after that bad week running nearly non-stop started making an ominous unbalanced low warble from its exhaust fan. This got to the point where it was unable to clear its own condensate and a couple days where I had to empty the drain pan about every 12 hours, versus never having to before (not a great deal of humidity here). I like old computers, but I try not to accumulate old broken computers, and that goes double for old broken air conditioners. It was time for a replacement.
When sizing replacement A/C units, remember that in the United States manufacturers only reported the ASHRAE BTU cooling capacity until 2017 (this is a nice explanation). The old LG was a 11,000 BTU unit (LP1111WXR) using R-401A which I bought off-the-rack from Home Depot and installed and insulated the duct myself, suitable to cool the volume of a medium-sized bedroom. Or, a medium sized bedroom with a whole bunch of computers in it. Fortunately the heat doesn't end in my corner of sunny So Cal until around November, so when I went shopping Home Depot still had a selection of portable A/Cs in stock even this "late" in the season. Although the new one I selected (an LG LP0721WSR) says it's "only" 7,000 BTU, that's actually using U.S. Department of Energy standards, which is the newer measurement. Convert it back to ASHRAE BTUs and it's a 12,000 BTU unit per LG's spec sheet — but full tilt pulls "just" 970W as opposed to the 1200W of the old unit, and is about 75% the size. The difference is not only better technology but the greater efficiency of R-32, requiring 40% less refrigerant for the same cooling and having almost 13% greater cooling capacity. Unlike vintage computers and vintage nerds, vintage air conditioning units just don't age well. (* A note here: Home Depot and LG are not sponsors. I'm just a customer telling you what I bought.)
It's important to keep in mind, though, that a consumer portable A/C unit is for normies. You know, people who play PS5 and watch Netflix and maybe read a book. Normies get up and fiddle with the controls and turn the A/C off when they leave. Normies don't have a room with an IBM POWER6, Sawtooth G4 (and its FireWire RAID), Mac mini G4, Macintosh IIci, Alpha Micro Eagle 300, Cobalt RaQ and associated IoT devices and network backbone infrastructure running non-stop (to say nothing of the Apple Network Server 500 and HP 9000/350 that also occasionally come out to play).The previous unit nevertheless worked well enough, even if occasionally it would be seized with a desire to freeze the room and plunge it down into the high 60s Fahrenheit until I noticed it or turned off its remotely controllable power outlet (which then caused it to forget its settings when it turned back on, though with a 72°F default setpoint it would at least "fail cool"). In particular it had a very nice energy saver feature which essentially turned off the fan entirely when the unit was idle: when a cooling cycle stopped, it shut down everything and was practically silent until the setpoint caused it to turn back on the compressor again. This meant I could leave the portable A/C on with the server room's door open overnight and use little power during early spring and late fall when it could passively cool, but not have its fan pointlessly add irregular air noise over the computers' orderly fan noise. After all, we're trying to sleep just a couple doors down. (In winter I simply turned it off completely.)
The new one doesn't have an "energy saver" mode per se, because it already has several energy saving features. Besides being more efficient in general, it can run its compressor at half-speed (the old one was either Arctic blast or bust), and automatically turns the fan down — but not off — between cooling cycles. The modes have distinctly different decibel volumes which can be easily distinguished on the USB audiometer, too. And it's paying off: I'm seeing about 15% less power usage compared to the old one despite similar outdoor temperatures during the past couple weeks. But it also makes it less liveable because its fan is more shrill and I can't turn it off completely without turning the entire unit off completely, and it also has a tendency to overchill the room due to heat patches I could see with my infrared thermometer (and since it's slightly more powerful than the old one, it can do so with even less effort). There weren't many options for repositioning it due to the exhaust duct, and putting in a small circulation fan to "help" guide cold air return to its thermostat didn't seem to do much of anything except make the room noisier. Yes, there's an automatic shutdown and startup timer, but that requires me to actually set it each time, and I'm a really lazy dude. Plus, even though I could remotely turn off its power outlet if necessary, doing so was basically the home power nerd equivalent of a rolling blackout and didn't seem particularly good for the hardware as a regular means of control.
At this point I realized the solution to my woes was literally in the palm of my hand:
Most portable A/Cs and split systems come with infrared remote controls, and so did this one. Surely there was some way to get the Sawtooth Power Mac G4 that was already recording the room temperature to send an IR signal to control the A/C unit ... right?To do this I would need something to actually receive the signal (an IR receiver), and then something to transmit it (an IR blaster). Because I was hoping to connect it to an old Power Mac running Mac OS X Tiger, that meant drivers were going to be an issue. On Linux many people doing this use lirc, and lirc supports a great many devices, but the MacPorts version supports rather fewer and doesn't seem to be tested on PowerPC at all. After looking around a few places I ran across irblaster.info, who sells a varied selection of IR doodads. Again, they aren't a sponsor, but it's clear this is a small business that takes pride in its products and Mike, the operator, replied to all my questions and had no problem with me (spoiler alert) hacking the units he builds and sells.
Notably in addition to the more typical USB and RS-232 units the site offers two Arduino Nano-based solutions, both a receiver and a blaster. The advantage of these is the Arduino has all the intelligence (using IRLib2, itself based on Ken Shirriff's IRemote — hi Ken!), so you just tell it what to send over a serial port; no mucking about with a lirc install is necessary. Because the Arduino Nano's USB serial port uses a CH340G, for which no drivers exist on Power Macs (there are drivers for Prolific and FTDI chipsets, but not those), I bought the true RS-232 version of the Arduino blaster. This provides a regular DE-9 serial port with a level shifter and I planned to use that with a PL-2303 USB to serial converter, for which I had known-working Power Mac drivers. The regular Arduino USB receiver with my Raptor Talos II workstation running Fedora would suffice for actually getting the IR codes.
This is the receiver. The Arduino Nano is neatly housed in a little 3-D printed case with a 30-60kHz receiver connected to its GPIO pins. It connected right up to my Talos II and appeared as /dev/ttyUSB1.IR encoding consists of groups of IR pulses at a chosen fixed signal frequency consolidated into marks (high) and spaces (low). This is necessary so that infrared radiation from other sources like, I dunno, you, doesn't trigger a sensor; it has to be presented as a specific sort of modulated signal. On top of that, IR-controlled devices have their own protocols such as how long a mark followed by a space or vice versa counts as a 1 or a 0, their particular identification and command sequences, and so on in order that they won't interfere with each other. You can always play back a raw sequence of marks and spaces but lirc and IRLib recognize specific manufacturer sequences and can generate them on the fly for particular commands.
Naïvely (because if I'd gotten this right, this would be a much shorter and less interesting story), I figured that the LG would do the same. I plugged in the device, started cu on that port at 9600bps, pointed the remote at it and pressed a random button to see what it would do.
% sudo cu -l/dev/ttyUSB1 -s9600 [sudo] password for censored: Connected. Ready to receive IR signals #define RAW_DATA_LEN 100 uint16_t rawData[RAW_DATA_LEN]={ 3118, 1562, 582, 1022, 586, 1014, 590, 222, 630, 226, 578, 262, 590, 990, 590, 262, 562, 266, 590, 990, 610, 998, 582, 266, 562, 1014, 594, 258, 542, 262, 590, 1014, 590, 1014, 590, 262, 566, 1014, 586, 1018, 586, 266, 562, 266, 562, 1014, 566, 266, 586, 262, 566, 1014, 562, 266, 590, 262, 562, 266, 562, 262, 542, 266, 586, 262, 566, 262, 566, 262, 562, 266, 538, 266, 586, 266, 562, 262, 566, 266, 562, 262, 566, 262, 566, 262, 566, 262, 566, 266, 562, 262, 562, 266, 562, 1018, 586, 262, 566, 262, 566, 1000}; Decoded Unknown(0): Value:0 Adrs:0 (0 bits) Raw samples(100): Gap:48610 Head: m3118 s1562 0:m582 s1022 1:m586 s1014 2:m590 s222 3:m630 s226 4:m578 s262 5:m590 s990 6:m590 s262 7:m562 s266 8:m590 s990 9:m610 s998 10:m582 s266 11:m562 s1014 12:m594 s258 13:m542 s262 14:m590 s1014 15:m590 s1014 16:m590 s262 17:m566 s1014 18:m586 s1018 19:m586 s266 20:m562 s266 21:m562 s1014 22:m566 s266 23:m586 s262 24:m566 s1014 25:m562 s266 26:m590 s262 27:m562 s266 28:m562 s262 29:m542 s266 30:m586 s262 31:m566 s262 32:m566 s262 33:m562 s266 34:m538 s266 35:m586 s266 36:m562 s262 37:m566 s266 38:m562 s262 39:m566 s262 40:m566 s262 41:m566 s262 42:m566 s266 43:m562 s262 44:m562 s266 45:m562 s1018 46:m586 s262 47:m566 s262 48:m566 Extent=55078 Mark min:538 max:630 Space min:222 max:1022
The Arduino Nano's default sketch as shipped in the receiver is a hybrid of IRLib2's demonstration raw receive and dump sketches, which emits the raw samples and then tells you if the library can make any sense out of it. However, it looks like IRLib apparently couldn't recognize the encoding (if it had, it would display it in the Decoded stanza). No problem, I said! I have the raw data and the blaster supports raw transmission, so I took this transcript and hooked up the RS-232 blaster to the Sawtooth G4 with the PL-2303 dongle. A separate USB plug provides power.
And here it is with the blaster dangling in front facing the A/C. The fire extinguisher is for the R-32. (Just kidding! It's actually for the computers! Still just kidding! Hopefully!) Mac OS X 10.4 does not have cu. It does have GNU screen, and that will work as a serial terminal, but I wanted something a little less like hitting a fly with a Buick and potentially more easily scriptable. Fortunately, there's a simple terminal program out there that will run on pretty much any computer ever made (I even used it myself on my Commodore 128 for dialup access before I got my Mac IIsi): good old Kermit. And of course Kermit comes in a single-binary all-in-one-file for Mac OS X 10.4 on PowerPC.By default the Arduino Nano in the blaster runs this sketch. Because it's just a little two-wire bitbanger and it doesn't echo what you type, we'll configure Kermit accordingly. It also communicates at 9600bps.
% kermit -l /dev/tty.usbserial -b 9600 C-Kermit 9.0.300 OPEN SOURCE:, 30 Jun 2011, for Mac OS X 10.4.11 Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. Type ? or HELP for help. (/Users/censored/) C-Kermit>set carrier-watch off (/Users/censored/) C-Kermit>set local-echo on (/Users/censored/) C-Kermit>connect Connecting to /dev/tty.usbserial, speed 9600 Escape character: Ctrl-\ (ASCII 28, FS): enabled Type the escape character followed by C to get back, or followed by ? to see other options. ---------------------------------------------------- irblaster.info RS232 IR TX 0.2 RAW,100,3118,1562,582,1022,586,1014,590,222,630,226,578,262,590,990,590,262,562,266,590,990,610,998,582,266,562,1014,594,258,542,262,590,1014,590,1014,590,262,566,1014,586,1018,586,266,562,266,562,1014,566,266,586,262,566,1014,562,266,590,262,562,266,562,262,542,266,586,262,566,262,566,262,562,266,538,266,586,266,562,262,566,266,562,262,566,262,566,262,566,262,566,266,562,262,562,266,562,1018,586,262,566,262,566,1000 RAW IR Sent
Aaaaaand ... nothing happened. Just to make sure I did it right, I plugged the receiver into my M1 MacBook Air (Apple provides CH340G drivers), took it into the server room, pointed the blaster at it and sent the sequence again. The receiver faithfully reported all the samples as entered but the A/C completely ignored them. So what gives?
One thing I did know is that unlike TVs and such, A/C and split system remotes don't send individual keypresses. Instead, to ensure everything is set correctly, every button press on the remote causes the entire state (mode, thermostat set point, fan speed, etc.) to be retransmitted to the unit. If the unit gets out of sync with the remote such as after, say, a power failure, the next time the remote sends a command, the unit will get the entire new state with that command and thus its settings will correctly match what the remote says they should be.
My working theory was that it wasn't sampling the full command, so I had to figure out how much capacity IRLib2 has for acquiring raw samples. It turns out by default it maxes out at 100, so odds were good my theory was correct because it would be very unusual indeed for a random vendor's remote to emit exactly that many samples. After increasing RECV_BUF_LENGTH to 255 I merged those receiver sketches and wrote my own, changing the format of the raw samples so that I just had to cut and paste to send to the blaster, and tested it with another sequence. This time we got something rather longer:
RAW,228,3122,1558,586,1018,586,1018,586,222,606,266,562,222,606,1018,586,266,538,262,566,1038,566,1042,586,226,578,1038,566,266,586,266,562,1018,586,1018,590,254,574,1014,562,1042,586,226,606,222,606,1014,590,222,582,262,590,1014,586,226,606,262,566,222,606,262,538,266,590,262,538,266,586,266,566,262,538,266,562,266,590,266,558,226,578,266,590,262,566,222,582,262,590,226,578,262,590,262,566,1014,590,262,562,266,538,1042,586,1018,590,262,566,222,606,262,562,226,606,262,562,226,606,262,538,266,590,1014,586,226,578,266,590,262,562,226,578,266,590,262,566,1014,586,226,578,1042,590,1014,590,1014,590,262,566,222,606,262,562,226,606,262,566,222,606,262,538,266,590,262,562,226,606,262,538,266,614,222,578,226,578,266,586,266,562,262,566,222,606,222,606,266,562,266,562,262,566,222,606,262,566,222,606,262,566,1014,590,222,606,262,566,262,566,1014,590,222,582,1038,590,1014,590,1014,590,1018,590,1010,590,222,606,262,566,266,562,1018,586,222,606 Decoded Unknown(0): Value:0 Adrs:0 (0 bits) Raw samples(228): Gap:13626 Head: m3122 s1558 0:m586 s1018 1:m586 s1018 2:m586 s222 3:m606 s266 4:m562 s222 5:m606 s1018 6:m586 s266 7:m538 s262 8:m566 s1038 9:m566 s1042 10:m586 s226 11:m578 s1038 12:m566 s266 13:m586 s266 14:m562 s1018 15:m586 s1018 16:m590 s254 17:m574 s1014 18:m562 s1042 19:m586 s226 20:m606 s222 21:m606 s1014 22:m590 s222 23:m582 s262 24:m590 s1014 25:m586 s226 26:m606 s262 27:m566 s222 28:m606 s262 29:m538 s266 30:m590 s262 31:m538 s266 32:m586 s266 33:m566 s262 34:m538 s266 35:m562 s266 36:m590 s266 37:m558 s226 38:m578 s266 39:m590 s262 40:m566 s222 41:m582 s262 42:m590 s226 43:m578 s262 44:m590 s262 45:m566 s1014 46:m590 s262 47:m562 s266 48:m538 s1042 49:m586 s1018 50:m590 s262 51:m566 s222 52:m606 s262 53:m562 s226 54:m606 s262 55:m562 s226 56:m606 s262 57:m538 s266 58:m590 s1014 59:m586 s226 60:m578 s266 61:m590 s262 62:m562 s226 63:m578 s266 64:m590 s262 65:m566 s1014 66:m586 s226 67:m578 s1042 68:m590 s1014 69:m590 s1014 70:m590 s262 71:m566 s222 72:m606 s262 73:m562 s226 74:m606 s262 75:m566 s222 76:m606 s262 77:m538 s266 78:m590 s262 79:m562 s226 80:m606 s262 81:m538 s266 82:m614 s222 83:m578 s226 84:m578 s266 85:m586 s266 86:m562 s262 87:m566 s222 88:m606 s222 89:m606 s266 90:m562 s266 91:m562 s262 92:m566 s222 93:m606 s262 94:m566 s222 95:m606 s262 96:m566 s1014 97:m590 s222 98:m606 s262 99:m566 s262 100:m566 s1014 101:m590 s222 102:m582 s1038 103:m590 s1014 104:m590 s1014 105:m590 s1018 106:m590 s1010 107:m590 s222 108:m606 s262 109:m566 s266 110:m562 s1018 111:m586 s222 112:m606 Extent=119730 Mark min:538 max:614 Space min:222 max:1042
Now we're getting somewhere! While IRLib2 still didn't know how to interpret the signals, the fact it didn't max out the sample buffer suggested it was getting the full sequence of pulses.
To see how I wanted to handle all this, I started with the remote with the power on, fan high and swing on at its current temperature of 81°F, and decided to see if I could make sense out of the pulses by generating minimal pairs. Ignoring the long mark and space at the beginning (used to prime the receiver's gain), and given that the mark durations were otherwise all very similar and the sequence was bookended completely by marks, it was likely that individual bits were being actually encoded by the spaces. A quick-and-dirty script to turn space durations into binary sequences, et voila:
1100010011010011011001001000000000000000000001001100000000100000101000000000000000000000000000001000101101001000 power off 1100010011010011011001001000000000000000001001001100000000100000101000000000000000000000000000001000101101101000 power on on ----^ 1100010011010011011001001000000000000000001001001100000000100000101000000000000000000000000000001000101101101000 swing off 1100010011010011011001001000000000000000001001001100000000100000101111000000000000000000000000001000101101110010 swing on ^^^---- swing on --------------------------^ 1100010011010011011001001000000000000000001001001100000011110000010000000000000000000000000000000011110110010000 med fan 1100010011010011011001001000000000000000001001001100000011110000101000000000000000000000000000000011110100110000 hi fan ^^^ 1100010011010011011001001000000000000000001001001100000011110000101000000000000000000000000000000011110100110000 60 F 1100010011010011011001001000000000000000001001001100000011110000101000000000000000000000000000001011110110110000 61 F 1100010011010011011001001000000000000000001001001100000010010000101000000000000000000000000000000001001101001000 72 F 1100010011010011011001001000000000000000001001001100000000100000101000000000000000000000000000001000101101101000 81 F 1100010011010011011001001000000000000000001001001100000001000000101000000000000000000000000000001010101100011000 85 F
There is a clearly conserved sequence at the beginning which undoubtedly represents the vendor identification. Power off and on are also easily distinguished with a simple change in one bit. It is interesting, though, that the vent swing and fan speed twiddle multiple bits, and the temperature seems to set values in two regions, potentially as a checksum (other historical LG A/C encodings have used CRCs in certain blocks, though these CRCs were based on multiple parts of the sequence, not just temperature). This is different from other LG encodings that have been documented, even for relatively recent units, and might also be based on vagaries of how I chose to interpret the signal.
However, we have a simpler way to handle control. I already know that setting the A/C to 81°F will easily cool the room well below 75°F (whether I want it to or not), and I also know it doesn't need to cool beyond that point. If the room is cooler than 75°, we'll just completely turn the A/C off. If it's over 80°F, turn it on to 81°F, high fan, no swing. As a fail-safe, if the room gets over 82°F, then set it to 60°F, high fan and no swing, and as the room cools back down it'll "just work" and turn it back to 81. This means we just need to acquire those particular sequences and be able to play them back.
As a test, let's see if we can transmit that long sequence we just acquired.
% kermit -l /dev/tty.usbserial -b 9600 C-Kermit 9.0.300 OPEN SOURCE:, 30 Jun 2011, for Mac OS X 10.4.11 Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. Type ? or HELP for help. (/Users/censored/) C-Kermit>set carrier-watch off (/Users/censored/) C-Kermit>set local-echo on (/Users/censored/) C-Kermit>connect Connecting to /dev/tty.usbserial, speed 9600 Escape character: Ctrl-\ (ASCII 28, FS): enabled Type the escape character followed by C to get back, or followed by ? to see other options. ---------------------------------------------------- RAW,228,3122,1558,586,1018,586,1018,586,222,606,266,562,222,606,1018,586,266,538,262,566,1038,566,1042,586,226,578,1038,566,266,586,266,562,1018,586,1018,590,254,574,1014,562,1042,586,226,606,222,606,1014,590,222,582,262,590,1014,586,226,606,262,566,222,606,262,538,266,590,262,538,266,586,266,566,262,538,266,562,266,590,266,558,226,578,266,590,262,566,222,582,262,590,226,578,262,590,262,566,1014,590,262,562,266,538,1042,586,1018,590,262,566,222,606,262,562,226,606,262,562,226,606,262,538,266,590,1014,586,226,578,266,590,262,562,226,578,266,590,262,566,1014,586,226,578,1042,590,1014,590,1014,590,262,566,222,606,262,562,226,606,262,566,222,606,262,538,266,590,262,562,226,606,262,538,266,614,222,578,226,578,266,586,266,562,262,566,222,606,222,606,266,562,266,562,262,566,222,606,262,566,222,606,262,566,1014,590,222,606,262,566,262,566,1014,590,222,582,1038,590,1014,590,1014,590,1018,590,1010,590,222,606,262,566,266,562,1018,586,222,606
This time, double nothing happened: I didn't even get the blaster's acknowledgement of RAW IR Sent. When I tried sending it again, I got invalid type instead.
Ah, I said, I get it. I need to increase the number of samples in its buffer to 255, and also increase the amount of characters it can take as part of one command (I must have overflowed its buffer). Time to crack open the blaster and program it.
Because this blaster has the RS-232 port connected, you need to remove the wires to the TX and RX pins before you can program it over USB. But there was an even more basic problem: Increasing the command and sample buffer exceeded the amount of working memory available (2KB, in the Arduino Nano). There was no way I was going to get that number of samples in one entry.So, if you can't get them in one command, put them in multiple commands. Samples are simply stored as uint16_t, so I could have a full array of those and use a series of shorter commands. Using fixed hex encoding to reduce the buffer size (since commas wouldn't be needed) and a command character to send or wait for more data, we add a couple hex bytes to set where in the sample buffer to deposit the samples and then use multiple commands to construct the buffer in pieces. Here's what I settled on for encoding a full set of samples to transmit off, 81°F, no swing, high fan:
P,00400BE6065E01FE044601FE044601FE013E01FA014201FE013E01FA044601FE014201FA013E01FE044601FE044601FE013E01FE044601FE013E01FA014201FA044A01FA044A01FA013E01FE044601FE044601FE013E01FE013E01FE044601FA014201FE013E01FA044A01FE013E01FE013E01FA013E01FE014201FA013E01FE013E P,408001FE013E01FA014201FE013E01FA014201FA013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013E01FA014201FA014201FA013E01FE044A01FA013E01FE013E01FE044601FE044601FE013E01FE013E01FE013E01FE013E01FA014201FE013E01FA014201FA014201FA044601FE013E01FE013E01FE013E01FE013E P,80C001FE013E01FA044A01FE013E01FA044601FE014201FA013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013A01FE014201FA013E01FE013E0202013A01FE013E01FE013E01FE013E01FE013A01FE014201FA013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013A01FE0142 S,C0E301FE013A01FE044601FE013E01FE013E01FE013E01FE044601FE013E01FA044601FE044601FE013E01FE044601FE013E01FE013E01FE044201FE013E02160126021601260216
The entire 227-count sample is generated section-by-section with this sequence of four commands. You can see the windows in the first two hex bytes (start and end+1 within the buffer), followed by up to 64 unsigned shorts as four hex nybbles each. The P is the command character for "partial" and the S for "send." By including the comma, it makes it visually distinct from the other commands that the blaster can accept, and doesn't match any of the blaster's existing commands which will still be accepted. The final end with the S command tells the blaster the total length of the samples to pass to IRLib to transmit. (IRLib2 includes a gap sample at index 0, but this isn't part of what we actually emit — we start with the long mark/space.)
Here are the Arduino sketches I used for the blaster and the receiver (you'll need IRLib2 to build them, and you'll need to have RECV_BUF_LENGTH to 255 in IRLib2/IRLibGlobals.h. Note that there are other known bugs in them and I'm just giving you what works for me. I also provide an updated tool (in Perl) to show you the binary sequence the hex commands encode; just provide those commands over standard input.
And here's what it looks like in Kermit:
% kermit -l /dev/tty.usbserial -b 9600 C-Kermit 9.0.300 OPEN SOURCE:, 30 Jun 2011, for Mac OS X 10.4.11 Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. Type ? or HELP for help. (/Users/censored/) C-Kermit>set carrier-watch off (/Users/censored/) C-Kermit>set local-echo on (/Users/censored/) C-Kermit>connect Connecting to /dev/tty.usbserial, speed 9600 Escape character: Ctrl-\ (ASCII 28, FS): enabled Type the escape character followed by C to get back, or followed by ? to see other options. ---------------------------------------------------- irblaster.info RS232 IR TX 0.2ck P,00400BE6065E01FE044601FE044601FE013E01FA014201FE013E01FA044601FE014201FA013E01FE044601FE044601FE013E01FE044601FE013E01FA014201FA044A01FA044A01FA013E01FE044601FE044601FE013E01FE013E01FE044601FA014201FE013E01FA044A01FE013E01FE013E01FA013E01FE014201FA013E01FE013E Packet Stored P,408001FE013E01FA014201FE013E01FA014201FA013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013E01FA014201FA014201FA013E01FE044A01FA013E01FE013E01FE044601FE044601FE013E01FE013E01FE013E01FE013E01FA014201FE013E01FA014201FA014201FA044601FE013E01FE013E01FE013E01FE013E Packet Stored P,80C001FE013E01FA044A01FE013E01FA044601FE014201FA013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013A01FE014201FA013E01FE013E0202013A01FE013E01FE013E01FE013E01FE013A01FE014201FA013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013A01FE0142 Packet Stored S,C0E301FE013A01FE044601FE013E01FE013E01FE013E01FE044601FE013E01FA044601FE044601FE013E01FE044601FE013E01FE013E01FE044201FE013E02160126021601260216 HEX IR Sent
And with a triumphant beep, the A/C turned off! Success!
Our last step is to automate this process. A cron job runs every 10 minutes to add another entry to the temperature and humidity log. We'll just add some extra code to "do something" based on the temperature.
The "something" will be to automate the process with Kermit, and for that we'll use another tool that comes with OS X 10.4, the ever-useful expect. Here's our expect script to automate sending the off sequence through Kermit.
#!/usr/bin/expect spawn /home/censored/bin/kermit -l /dev/tty.usbserial -b 9600 expect "C-Kermit>" send "set carrier-watch off\r" expect "C-Kermit>" send "set local-echo on\r" expect "C-Kermit>" send "connect\r" expect -- "-----\r\n" send "\r\rP,00400BE6065E01FE044601FE044601FE013E01FA014201FE013E01FA044601FE014201FA013E01FE044601FE044601FE013E01FE044601FE013E01FA014201FA044A01FA044A01FA013E01FE044601FE044601FE013E01FE013E01FE044601FA014201FE013E01FA044A01FE013E01FE013E01FA013E01FE014201FA013E01FE013E\r" expect "Stored\r" send "P,408001FE013E01FA014201FE013E01FA014201FA013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013E01FA014201FA014201FA013E01FE044A01FA013E01FE013E01FE044601FE044601FE013E01FE013E01FE013E01FE013E01FA014201FE013E01FA014201FA014201FA044601FE013E01FE013E01FE013E01FE013E\r" expect "Stored\r" send "P,80C001FE013E01FA044A01FE013E01FA044601FE014201FA013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013A01FE014201FA013E01FE013E0202013A01FE013E01FE013E01FE013E01FE013A01FE014201FA013E01FE013E01FE013E01FE013E01FE013E01FE013E01FE013A01FE0142\r" expect "Stored\r" send "S,C0E301FE013A01FE044601FE013E01FE013E01FE013E01FE044601FE013E01FA044601FE044601FE013E01FE044601FE013E01FE013E01FE044201FE013E02160126021601260216\r" expect "Sent\r" send "S,C0E301FE013A01FE044601FE013E01FE013E01FE013E01FE044601FE013E01FA044601FE044601FE013E01FE044601FE013E01FE013E01FE044201FE013E02160126021601260216\r" expect "Sent\r" send "\034C" expect "C-Kermit>" send "quit\r"
If you're unfamiliar with an expect script, the basic notion is to start a dependent program and control it as if you were typing into it from a terminal. You tell expect what to expect (hence the name), and then tell it what to send. Here, we start Kermit and wait for its prompt, set the terminal settings appropriately, and then send the sequences, waiting for Stored after each packet and Sent after the final one. The "\034C" sequence sends CTRL-backslash and a C to Kermit to exit terminal mode so that we can quit cleanly. There are three scripts, one to turn it off and one to turn it on at 81°F (high fan, no swing), and one to turn it on at 60°F (high fan, no swing), which the cron job chooses between.
There are a couple bits of paranoia in these scripts for reliability. \r is the same as it is in C, i.e., as if you had sent a RETURN character. We do it twice at the beginning of sending the first packet in case there are any partial commands still in the buffer (we'll just get an invalid type if it's unrecognized, which this script ignores). At the end we also give the final send command twice: the buffer is still intact, and this ensures that any glitch about the A/C receiving the infrared signal is likely compensated for. Since the A/C command sequences are idempotent by dint of specifying the entire desired state, transmitting the same sequence twice doesn't harm anything.
One last wrinkle: we have a special case in our temperature algorithm for whipping the A/C harder if the room isn't getting cooler, but if the room is already below the minimum temperature (like in the middle of winter), the cron job will faithfully still send an "off" sequence anyway — even if the A/C was off to begin with. This wouldn't be a problem except that this makes the unit beep in response over and over every 10 minutes, which is not nice when you're sleeping and the server room door is open to passively cool.
To handle this, the cron job simultaneously notes the name of the last expect script it ran. We want to resend the cooling sequences at 80°F+ and 82°F+ to ensure the A/C gets flogged to get the temperature down, but if the last script that was run was the off script, then we suppress running it again until some other script is run. On the unlikely chance the initial off signals (we send them twice) were never received, it is likely that the human who lives in the house and pays the electric bill and notices the A/C running full blast in the server room turning it into Longyearbyen will simply turn the unit off himself.
You know, like normies. Normies with a vintage server room. As you do.
No comments:
Post a Comment
Comments are subject to moderation. Be nice.