Dangerous Things
Custom gadgetry for the discerning hacker

The Store is now open! Check out the gadgetry »
Like what you're reading?
Share It.

Using an EMC AX150 or AX150i SAN without the APC UPS

I’ve got a couple EMC AX150i iSCSI SAN boxes I use for network storage. They are designed to function with write-caching turned on when they are in contact with the 1U APC UPS they ship with. When you buy one of these SAN units new, the UPS and special communications cable comes with it pretty much as standard parts. The problem is, if you buy one of these SANs on the used market like I did you pretty much have to scrounge your own UPS and cable, otherwise write-caching is turned off and you get truly horrible write performance. I don’t happen to use APC UPS gear in my racks for power protection, so I had to figure out a way to emulate an APC and tie that emulation in with the power protection hardware I did use.

Luckily I found someone nice enough to post both the communications cable pin-outs and serial conversation details that normally go between an AX150 SAN and the APC UPS it’s attached to. I used information this as the basis for my emulator, but just in case the original post goes away and the wayback machine fails us, I’m reposting it here (pdf).

Also! A reader (hi Jeff!) went ahead and created a “UPS emulator” with PIC microprocessor and circuit layout and source code and everything! Download the ZIP file here.

The Cables

Serial console cable

If you are looking for the pinout of the ax150i console cable; click here or just buy it on Amazon.

The UPS cable

The UPS cable is a little harder to find online, so you may need to recreate it using this pinout diagram:

The Software

Another reader named Jon posted a comment below with a link to a compiled version written in C#.

========================================== CODE ==========================================

You can find the APC SmartUPS commands off the web.

Communications capture

Normal Comms from/to AX-150

Y >>
SM<0D><0A>
Q >>
08<0D><0A>
f >>
100.0<0D><0A>
R >>
BYE<0D><0A>

Y >>
SM<0D><0A>
Q >>
08<0D><0A>
f >>
100.0<0D><0A>
R >>
BYE<0D><0A>

!!! Weekly Batt Test Time !!!!!

Y >>
SM<0D><0A>
Q >>
08<0D><0A>
f >>
100.0<0D><0A>
R >>
BYE<0D><0A>
Y >>
SM<0D><0A>
W >> !! Tests battery, like pushing the test button on the front panel
OK<0D><0A>
R >>
BYE<0D><0A>
Y >>
SM<0D><0A>
Q >>
08<0D><0A>
f >>
100.0<0D><0A>
R >>
BYE<0D><0A>
Y >>
SM<0D><0A>
Q >>
08<0D><0A>
f >>
100.0<0D><0A>
X >> !! Self-test results
OK<0D><0A>
l >> !! Low transfer voltage
097<0D><0A>
R >>
BYE<0D><0A>

Y >>
SM<0D><0A>
Q >>
08<0D><0A>
f >>
100.0<0D><0A>
R >>
BYE<0D><0A>
Y >>
SM<0D><0A>
n >> !! Get Serial number
AS0632112344<00><0D><0A>
R >>
BYE<0D><0A>

!!! End of Test !!!

Y >>
SM<0D><0A>
Q >>
08<0D><0A>
f >>
100.0<0D><0A>
R >>
BYE<0D><0A>

Y >>
SM<0D><0A>
Q >>
08<0D><0A>
f >>
100.0<0D><0A>
R >>
BYE<0D><0A>

Y >>
SM<0D><0A>
Q >>
08<0D><0A>
f >>
100.0<0D><0A>
R >>
BYE<0D><0A>

Y >>
SM<0D><0A>
Q >>
08<0D><0A>
f >>
100.0<0D><0A>
R >>
BYE<0D><0A>

Delphi snapshot of main command-response logic:

if Result = pmerGood then
begin
tRx := Engine.RxString;

if cbHex.Checked then
Msg(Engine.SeeBytes(tRx)+ ‘ >> ‘ + #$0D + #$0A)
else
Msg(Engine.SeeChars(tRx)+ ‘ >> ‘ + #$0D + #$0A);

PendingCmd := ”;
Engine.CharDelay := 0;

CmdData := ‘NA’ + #$0D + #$0A;
if length(tRx) > 0 then
case ord(tRx[1]) of
$01 : CmdData := ‘Smart-UPS 750 RM’ + #$0D + #$0A;
$1A : CmdData := ‘#uD43127130133136uA43108110112114uI43253257261265uM432252292’ + ‘33237lD43106103100097lA43092090088086lI43208204200196lM43182178174170e47200153045607590oD13120oA13100oI33230240220oM13208s431HMLq4820205081114172023p483090180270360450540630000k4310TNr483000060120180240300360420E443336168ON OFF’ + #$0D + #$0A;
ord(‘A’) : CmdData := ‘OK’ + #$0D + #$0A;
ord(‘B’) : CmdData := ‘27.40’ + #$0D + #$0A;
ord(‘C’) : CmdData := ‘016.2’ + #$0D + #$0A;
ord(‘D’) : begin CmdData := ‘!!!$’; Engine.CharDelay := 600; end;
ord(‘E’) : CmdData := ‘336’ + #$0D + #$0A;
ord(‘F’) : CmdData := ‘60.00’ + #$0D + #$0A;
ord(‘G’) : CmdData := ‘S’ + #$0D + #$0A;
ord(‘I’) : CmdData := ‘FF’ + #$0D + #$0A;
ord(‘J’) : CmdData := ‘0F,00’ + #$0D + #$0A;
ord(‘K’) : CmdData := ‘OK’ + #$0D + #$0A;
ord(‘L’) : CmdData := ‘126.7’ + #$0D + #$0A;
ord(‘M’) : CmdData := ‘128.1’ + #$0D + #$0A;
ord(‘N’) : CmdData := ‘125.2’ + #$0D + #$0A;
ord(‘O’) : CmdData := ‘113.0’ + #$0D + #$0A;
ord(‘P’) : CmdData := ‘000.0’ + #$0D + #$0A;
ord(‘Q’) : CmdData := ’08’ + #$0D + #$0A;
ord(‘R’) : CmdData := ‘BYE’ + #$0D + #$0A;
ord(‘S’) : CmdData := ‘OK’ + #$0D + #$0A;
ord(‘U’) : begin CmdData := ‘!!!$’; Engine.CharDelay := 600; end;
ord(‘V’) : CmdData := ‘FWD’ + #$0D + #$0A;
ord(‘W’) : CmdData := ‘OK’ + #$0D + #$0A;
ord(‘X’) : CmdData := ‘OK’ + #$0D + #$0A;
ord(‘Y’) : CmdData := ‘SM’ + #$0D + #$0A;
ord(‘a’) : CmdData := ‘3.!$%+?=#|.’ + #$01 + #$0E + #$1A + ”’)+-89>@ABCDEFGKLMNOPQRSUVWXYZabcefgjklmnopqrsuxyz~’ + #$0D + #$0A;
ord(‘b’) : CmdData := ‘615.3.D’ + #$0D + #$0A;
ord(‘c’) : CmdData := ‘UPS_IDEN’ + #$0D + #$0A;
ord(‘e’) : CmdData := ’00’ + #$0D + #$0A;
ord(‘g’) : CmdData := ‘024’ + #$0D + #$0A;
ord(‘f’) : CmdData := ‘100.0’ + #$0D + #$0A;
ord(‘i’) : CmdData := ‘FWD’ + #$0D + #$0A;
ord(‘j’) : CmdData := ‘0375:’ + #$0D + #$0A;
ord(‘k’) : CmdData := ‘0’ + #$0D + #$0A;
ord(‘l’) : CmdData := ‘097’ + #$0D + #$0A;
ord(‘m’) : CmdData := ’08/03/06′ + #$0D + #$0A;
ord(‘n’) : CmdData := ‘AS0632112344’ + #$00 + #$0D + #$0A;
ord(‘o’) : CmdData := ‘120’ + #$0D + #$0A;
ord(‘p’) : CmdData := ‘090’ + #$0D + #$0A;
ord(‘q’) : CmdData := ’02’ + #$0D + #$0A;
ord(‘r’) : CmdData := ‘000’ + #$0D + #$0A;
ord(‘s’) : CmdData := ‘H’ + #$0D + #$0A;
ord(‘u’) : CmdData := ‘127’ + #$0D + #$0A;
ord(‘x’) : CmdData := ’01/01/10′ + #$0D + #$0A;
ord(‘y’) : CmdData := ‘(C) APCC’ + #$0D + #$0A;
ord(‘z’) : CmdData := ‘CLEAR’ + #$0D + #$0A;
ord(‘9’) : CmdData := ‘FF’ + #$0D + #$0A;
end;

if CmdData <> ” then
begin
if cbHex.Checked then
Msg(Engine.SeeBytes(CmdData)+ #$0D + #$0A)
else
Msg(Engine.SeeChars(CmdData)+ #$0D + #$0A);

Engine.SendMsg := CmdData;
end;

Leave a Reply