Reverse engineering Hitachi air conditioner infrared remote commands

2015-03-29 4 comments

Updated 2 April 2015


As part of a home automation project me and a friend started to reverse engineer the infrared remote control protocol used by a Hitachi split type air conditioner. The model in question was a RAS-25SX8 (manual here) but there is no reason to believe that the protocol isn’t the same for many other models and the methods we used to reverse engineer the codes should work with pretty much any remote control.

Most remote controls or at least the remote controls we’ll be talking about here work by sending a series of 38KHz PWM pulses mixed with pauses of various length.
Finding out the length of the PWM pulses and the length of the pauses between them is the key to cracking the protocol.
A remote control for a TV usually sends a code of one byte or so telling which key the user is pressing and it keeps sending the same code over and over until the key is released. There is a different code for each key on the remote control.
Air conditioner codes on the other hand are quite long because it’s the remote control that keeps track of the settings and it sends the complete set of settings everytime a key is pressed.

The only information we managed to find about any Hitachi remote control protocols was this blog:

They managed to turn a device on or off using recorded codes but provide no source code so we couldn’t quite wrap our heads around how to send the codes. We also wanted to do a bit more than just turning on and off so we started to dig.

Aquiring authentic commands

We first needed to take a look at code from a real remote control to get an understanding about how it’s constructed.

As suspected and as mroxanas mentioned, the codes used are quite long – much longer than we managed to record using Arduino and some standard IR libraries such as Arduino-IRremote.
In the end we decided to go with an Arduino sketch from with which we managed to record several long codes which all seemed consistent enough to be correct recordings. The sketch can be found at

The AnalysIR sketch dumps puls lengths and pause lengths in microseconds like this:

Raw: (595) 3280, -1704, 468, -1168, 472, -400, 448, -428, 444, -428, 444, -428, 444, -428, 448, -428, 444, -432, 444, -428, 444, -432, 444, -428, 444, -428, 444, -1192, 468, -404, 444, -428, 444, -436, 440, -432, 444, -428, 444, -428, 444, -428, 444, -432, 444, -428, 444, -428, 444, -436, 440, -432, 444, -432, 440, -428, 444, -432, 444, -428, 444, -428, 444, -1188, 472, -408, 444, -1188, 448, -1188, 444, -1188, 448, -1188, 444, -1192, 444, -1188, 444, -432, 444, -1192, 444, -1192, 444, -1188, 444, -1192, 444, -1188, 448, -1188, 444, -1188, 448, -1188, 444, -1196, 444, -428, 444, -428, 444, -432, 440, -432, 444, -428, 444, -428, 444, -432, 444, -432, 444, -428, 444, -428, 444, -1192, 444, -1192, 444, -428, 444, -428, 444, -1192, 444, -1192, 448, -1188, 444, -1188, 448, -428, 444, -428, 444, -1188, 448, -1188, 444, -428, 444, -436, 444, -1188, 472, -1164, 444, -1188, 472, -404, 444, -428, 444, -1188, 448, -428, 444, -1192, 444, -432, 444, -428, 444, -428, 444, -1188, 448, -1188, 448, -428, 444, -1188, 444, -432, 444, -432, 444, -428, 444, -1188, 448, -428, 444, -428, 444, -428, 444, -1192, 444, -432, 444, -1192, 444, -1188, 444, -428, 448, -1188, 444, -1188, 448, -1188, 444, -432, 444, -1192, 444, -432, 440, -432, 444, -1188, 472, -400, 448, -428, 444, -1188, 444, -1192, 468, -408, 444, -1192, 468, -1164, 448, -428, 444, -1188, 444, -1192, 468, -404, 444, -428, 444, -1196, 444, -428, 444, -428, 444, -432, 444, -428, 444, -428, 444, -428, 444, -432, 444, -432, 444, -1188, 472, -1164, 444, -1188, 472, -1164, 444, -1188, 448, -1188, 448, -1188, 472, -1164, 448, -428, 444, -428, 444, -428, 444, -432, 440, -432, 444, -428, 444, -428, 444, -436, 444, -1188, 444, -1192, 444, -1188, 448, -1188, 444, -1188, 448, -1188, 444, -1188, 448, -1192, 444, -432, 440, -432, 444, -428, 444, -428, 444, -428, 444, -432, 444, -428, 444, -432, 444, -1192, 444, -1188, 448, -1188, 464, -1172, 444, -1188, 444, -1192, 444, -1188, 444, -1196, 444, -428, 444, -428, 444, -432, 444, -428, 444, -428, 444, -428, 444, -432, 444, -432, 444, -1188, 472, -1164, 444, -1192, 444, -1188, 444, -1192, 444, -1188, 448, -1188, 444, -1192, 448, -428, 444, -428, 444, -428, 444, -432, 444, -428, 444, -428, 444, -432, 440, -436, 444, -1188, 444, -1192, 444, -1188, 448, -1188, 444, -1188, 448, -1188, 444, -1192, 444, -1192, 444, -432, 444, -1188, 444, -1192, 444, -428, 444, -1188, 448, -428, 444, -1188, 448, -432, 444, -1188, 444, -432, 444, -428, 444, -1188, 448, -428, 444, -1188, 444, -428, 444, -1196, 444, -1188, 448, -428, 444, -428, 444, -432, 440, -1192, 444, -1188, 448, -1188, 444, -1192, 448, -428, 444, -1192, 444, -1188, 444, -1192, 444, -428, 444, -428, 444, -428, 444, -436, 444, -428, 444, -428, 444, -432, 444, -428, 444, -428, 444, -428, 444, -432, 440, -436, 444, -1188, 448, -1188, 444, -1192, 444, -1188, 444, -1192, 444, -1188, 464, -1172, 444, -1192, 448, -428, 444, -428, 444, -428, 444, -432, 440, -432, 444, -428, 444, -428, 444, -436, 440, -1192, 444, -1192, 444, -1188, 444, -1192, 444, -1188, 448, -1188, 444, -1188, 448, -1192, 444, -428, 444, -432, 444, -428, 444, -428, 444, -432, 440, -432, 444, -428, 444, -432, 444, -1192, 444, -1188, 448, -1188, 444, -1192, 444, -1188, 444, -1192, 444, -1188, 444, -1196, 468, -1164, 448, -1188, 444, -1192, 444, -1188, 444, -432, 440, -432, 444, -428, 444, -432, 444, -432, 440, -432, 444, -428, 444, -428, 444, -1192, 444, -1192, 444, -1188, 444, -1192, 448,

The command in this example contained the settings for Heat mode, 25 C degrees, Auto fan speed.

Analysing the pulse format

The text “Raw: (595)” is not a part of the code. It’s just part of the output from AnalysIR telling us type and length of the captured code. The code starts with 3280.
Positive numbers are pulses (38KHz PWM pulse) and negative numbers are pauses (silence). The number indicates the duration in microseconds (us).
If we look at the beginning of the code it differs a bit from the rest of the code.
3280 is a pulse with a length of 3280 us. It’s followed by -1704 which is a pause for 1704 us.
The code continues with 468 us pulse, 1168 us pause, 472 us pulse, 400 us pause, a new pulse and another pause and so on for the rest of the code. The code must always end with a pulse. The last pause wouldn’t be a pause if it didn’t have a pulse after it, would it?
The exact duration of the pulses and pauses varies a little bit between the recordings but not significantly. They do however differ a bit from the numbers in mroxanas blog. Our guess is that the relation between the lengths are more important than the actual lengths.

All the pulses are roughly the same length. Our average was around 450us.
The pauses are either long like 1168us or short like 400us.
Our averages were around 1180us and 400us so that’s the numbers we used in our tests and they worked well.
Here’s all the lengths:
First pulse: 3200 us
First pause: 1700 us
Pulse: 450 us
Long pause: 1180 us
Short pause: 400 us

The first pulse and pause is some start code.
The rest of the pauses are the bits that make out the data in the command. The pulses are just there to separate the pauses from eachother.
A long pause is binary one
A short pause is binary zero

The amount of bits is normally 296 (37 bytes). There seems to be some extra bits on the end if you use an alternate address setting on the remote control.

The data captured by the AnalysIR could be used as it is with the IRremote library’s sendRaw function. But to crack the protocol and gain higher flexibility when sending commands it’s neccessary to perform some deeper analysis. We must therefore conver all the raw data to bits.
We did this with a quick and dirty powershell script that takes the input from the AnalysIR sketch, strips out all the positive numbers and converts all the negative numbers to either 1 or 0 depending on how long they are. If it’s > -800 it’s a 0 and if it’s < -800  it’s a 1.

The code converted to binary:


Analyzing protocol data

To get a better overview of the data it would make sense to divide the data into bytes if that’s the way it’s organized.

       1        2        3        4        5        6        7        8        9       10       11       12       13       14       15       16       17       18       19       20       21       22       23       24       25       26       27       28       29       30       31       32       33       34       35       36       37
10000000 00001000 00000000 00000010 11111101 11111111 00000000 00110011 11001100 11100101 00011010 00100010 11011101 00100110 11011001 00000000 11111111 00000000 11111111 00000000 11111111 00000000 11111111 00000000 11111111 01101010 10010101 10001111 01110000 00000000 11111111 00000000 11111111 00000000 11111111 11110000 00001111

It’s quite obvious that the binary data makes out even bytes, just look at bytes 16-25.
It’s also obvious that every second byte starting with byte 5 has got all the bits from the previous byte inverted (bitwise NOT). So bits 4,6,8,10,12 etc are bytes with data while 5,7,9,11 etc are just parity bits.

To figure out what all the data is and where your settings are stored it’s neccessary to record several codes with only one change at a time and compare them to eachother so let’s compare the above example of 25 C temperature with 26 and 27 degrees:

            1        2        3        4        5        6        7        8        9       10       11       12       13       14       15       16       17       18       19       20       21       22       23       24       25       26       27       28       29       30       31       32       33       34       35       36       37
25C  10000000 00001000 00000000 00000010 11111101 11111111 00000000 00110011 11001100 11100101 00011010 00100010 11011101 00100110 11011001 00000000 11111111 00000000 11111111 00000000 11111111 00000000 11111111 00000000 11111111 01101010 10010101 10001111 01110000 00000000 11111111 00000000 11111111 00000000 11111111 11110000 00001111
26C  10000000 00001000 00000000 00000010 11111101 11111111 00000000 00110011 11001100 11100101 00011010 00100010 11011101 00010110 11101001 00000000 11111111 00000000 11111111 00000000 11111111 00000000 11111111 00000000 11111111 01101010 10010101 10001111 01110000 00000000 11111111 00000000 11111111 00000000 11111111 11110000 00001111
27C  10000000 00001000 00000000 00000010 11111101 11111111 00000000 00110011 11001100 11100101 00011010 00100010 11011101 00110110 11001001 00000000 11111111 00000000 11111111 00000000 11111111 00000000 11111111 00000000 11111111 01101010 10010101 10001111 01110000 00000000 11111111 00000000 11111111 00000000 11111111 11110000 00001111

There are only two bytes that changes – byte 14 and 15. Byte 15 is just parity so it’s byte 14 that contains the temperature.
25C: 00100110
26C: 00010110
27C: 00110110
If you know your binary numbers you will see that part of these bytes actually contain the temperature. The binary representation of 25 is 11001 but in the data from the remote it is written with the least significant bit first (LSB first) – 10011. in other words backwards.
After more tests it’s clear that the temperature is sent in byte 14 (and 15) no matter what mode the unit is operating in so that’s what byte 14 is used for.
The other bits of byte 14 has never changed in our tests so they are probably always 0.

We went through some basic settings to map out other functions and see what number correspond to certain codes but we have only been scratching the surface.

Mapped bytes

These are the bytes and bits that we know anything about today.


We are quite sure about the green ones while we are just assuming that the orange ones are static.
The white bits are totally unknown or just indications which we haven’t investigated yet. Feel free to do so and let us know what you find in the comments.

Byte 12

Seems to be used differently by all remote control operations.
Combinations of bits seems to activate/deactivate different operations or modes but not all operations need byte 12 to work.

Observered variants
Operation Bits In combination with
Adding a timer 01000100 Minutes in bytes 18-24 and Timer type in byte 24 – bit 5 or 6
Removing a timer 00100100 Timer type in byte 24 – bit 5 or 6
Change fan speed 01000010 Byte 26
Change temperature 00100010 Byte 14
Plasma air purifying operation 10010010 Byte 28 – bit 8 = 0 (questionable)
Mold monitor operation 11010010 Byte 34 – bit 7 = 1
Internal cleaning operation 11110010 Byte 34 – bit 4 = 1
Ionized mist operation 00010110 Byte 30 – bit 1 = 1
Sleep operation 11010110 Byte 32 – bit 5 = 1
Toggle(?) vertical air deflector movement 10000001
Adjust horizontal air deflectors 00110001 Byte 10?
Air quality monitor operation 01000011 Byte 34 – bit 3 = 1

Some of these operations like fan and temperature don’t need a specific code in this byte because they are all being set every time a code is sent. Is the same true for mist, sleep, monitoring modes and horizontal deflectors etc?
We have not tried to replicate all of these operations.
Must investigate further.

Byte 13

Bitwise NOT of Byte 12.

Byte 14

Bits 1-2: Always 0
Bits 3-7: Room temperature setting in Celsius. Possible values:  16 to 32 for most modes, 10 to 32 for dehumidify.
Bit 8: Always 0

Bit 3 is LSB and bit 7 MSB so 22 degrees would be written 01101
Fill out with the other bits and you get 00011010.

Byte 15

Bitwise NOT of Byte 14.
At 22 degrees it would be 11100101

Byte 16

Possibly related to timers.

Byte 18+20

These bytes are used together to set a time for an OFF TIMER
The time is specified in minutes from now and stored as an LSB first 11 or 12-bit number starting at Byte 18, Bit 5 and ending in Byte 20, Bit 7 (or 8?).

Byte 18, Bit 1-4: Seems to always be 0000
Byte 18, Bit 5-8 (5 is LSB)
Byte 20, Bit 1-7 (7 is MSB)
Byte 20, Bit 8: Possibly bit 12 (MSB bit) but most likely always 0 since no valid numbers need that man bits.


To specify 600 minutes.
600 binary = 1001011000
600 binary LSB first 0001101001
Byte 18 + 20 = xxxx0001 10100100

Byte 19+21

Bitwise NOT of bytes 18 and 20

Bytes 22+24

These bytes are used together to set a time for an ON TIMER
The time is specified in minutes from now and stored as an LSB first 11 or 12-bit number starting at Byte 22, Bit 1 and ending in Byte 24, Bit 3 (or 4?).
Two bits in byte 24 are also used to activate on timer or off timer or both (on/off timer).

Byte 22, Bit 1-8 (bit 1 is LSB)
Byte 24, Bit 1-3 (bit 3 is MSB)
Byte 24, Bit 4: Possibly bit 12 (MSB bit) but most likely always 0 since no valid numbers need that man bits.
Byte 24, Bit 5: Activate off timer when set to 1
Byte 24, Bit 6: Activate on timer when set to 1


To specify 600 minutes.
600 binary = 1001011000
600 binary LSB first 0001101001
Byte 22 + 24 = 00011010 0100xxxx

Byte 23+25

Bitwise NOT of bytes 22 and 24

Byte 26

Modes + Fan speed
Bits 1-4: Mode setting. Possible values: Not fully known
Bits 5-7: Fan speed. Possible values: 1 (silent) to 5 (automatic)
Bit 8: Always 0


All 16 binary combinations listed even though all aren’t known and som may not be valid.

Setting No. Bit 1 Bit 2 Bit 3 Bit 4 Mode
0 0 0 0 0 ?
1 1 0 0 0 ?
2 0 1 0 0 ?
3 1 1 0 0 Cool
4 0 0 1 0 Dry Cool
5 1 0 1 0 Dehumidify
6 0 1 1 0 Heat
7 1 1 1 0 Automatic Operation
8 0 0 0 1 ?
9 1 0 0 1 Auto Dehumidifying
10 0 1 0 1 Quick Laundry
11 1 1 0 1 ?
12 0 0 1 1 Condensation Control
13 1 0 1 1 ?
14 0 1 1 1 ?
15 1 1 1 1 ?
Fan speed
Setting No. Bit 5 Bit 6 Bit 7 Fan Speed
1 1 0 0 Silent
2 0 1 0 Low
3 1 1 0 Med
4 0 0 1 Hi
5 1 0 1 Auto
Byte 27

Bitwise NOT of Byte 26.
If Byte 26 is set to Mode=Cool and Fan=Hi (11000010), Byte 27 would have to be 00111101.

Byte 28

Power On/Off + Sleep timer(?)
Partly unknown.

Bit 1-4: Has always been 1000 in our tests.
Bit 5: Turn unit on (1) or off (0). Should always be 1 when changing other settings.
Bit 6-7: Has always been 11 in our tests.
Bit 8: Function is not investigated but turns to 0 when sleep timer is activated. Otherwise 1.


10001111: Power on unit or keep unit on when changing settings
10000111: Power off.

Byte 29

Bitwise NOT of Byte 28

Byte 30

Bit 5-6: Set to 11 to enable Powerful Operation (not tested)

Byte 31

Bitwise NOT of Byte 30

Byte 36

Bit 1-4: Has always been 1111 in our tests.
Bit 5-8: Humidity. Possible values: 3 (40%) to 9 (70%)

Setting No. Bit 5 Bit 6 Bit 7 Bit 8 Humidity
3 1 1 0 0 40%
4 0 0 1 0 45%
5 1 0 1 0 50%
6 0 1 1 0 55%
7 1 1 1 0 60%
8 0 0 0 1 65%
9 1 0 0 1 70%
Byte 37

Bitwise NOT of Byte 36

More bytes and bits will be added if we find any. If you have any additions or corrections please leave a comment.

Controlling the unit using Arduino

Now that we have a few known commands it’s possible to control the individual functions of the unit.
We need to send the full 37 bytes every time so it’s necessary to have a complete default command to fill out the areas that we don’t know much about. We can then replace selected parts of that default command to create a full custom command.

We went with one of the examples above as a starting point.
Heat, 25 degrees, Fan=Auto.


We put all the bits from the default command into an array. Then we replace the necessary bits with the values we want. If we want to change the temperature we need the binary LSB first representation of the temperature. That’s five bits.
We then replace the bits with offset 106-110 in our array with the five bits representing the new temperature.
If everything else in the default command is ok we can send the command now and the temperature will change.

Sending the command means doing a lot of pulsing at 38Khz. A pulse can be sent from arduino by toggling a pin with an IR LED between HIGH and LOW every 26us.
A function like this can do that and takes the length of the pulse as an argument:

void sendPulse(int pulseLength){
    int IR_on = 0;
    long startMicros;
    startMicros = micros();
    while (micros() &amp;amp;amp;amp;lt; (startMicros + pulseLength)){
        // sending 38 KHz pulse
        if (IR_on == 0) {
            IR_on = 1;
        else {
            IR_on = 0;
        digitalWrite(IRPIN, IR_on);
    // turn off IR after pulse is complete
    digitalWrite(IRPIN, LOW);

According to what we learned from our analysis of the protocol we would have to begin the command with a special pulse and a special pause, then standard pulses and delays for either 0 or 1.

&amp;amp;amp;amp;lt;hundreds of bits&amp;amp;amp;amp;gt;

Of course you wouldn’t code it like this.
Instead you loop through an array with all the bits that needs to be sent as mentioned earlier and you just send the special pulses using a teqnique such as the example above.

Download a complete example sketch for sending a command here.

Here’s another sketch that takes commands from the serial monitor on the Arduino. It converts the supplied values and applies them to the right positions in the command and then sends the command.

Categories: Arduino, Projects

Gather HP iLO information using PowerShell

2014-03-11 9 comments

Please begin by reading my previous post where I show how to retrieve physical hard disk information such as model numbers and firmware versions using the HP Insight Management WBEM providers.

A few days ago, as HP published an advisory regarding firmware of their Integrated Lights-Out (iLO) management controllers I wanted a way to collect this information from a number of servers. I created another CmdLet to retrieve information from two associated classes.
Namespace: root\hpq
Classes: HP_MPFirmware and HP_ManagementProcessor

For full details about the properties in these classes as well as the rest of the HP WBEM classes, download the latest version of the PDF document HP Insight Management WBEM Providers for Microsoft Windows Data Sheet

The Get-HPiLOInformation function

function Get-HPiLOInformation
    Retrieves iLO management controller firmware information
    for HP servers.
    The Get-HPiLOInformation function works through WMI and requires
    that the HP Insight Management WBEM Providers are installed on
    the server that is being quiered.
    .PARAMETER Computername
    The HP server for which the iLO firmware info should be listed.
    This parameter is optional and if the parameter isn't specified
    the command defaults to local machine.
    First positional parameter.
    Lists iLO firmware information for the local machine
    Get-HPiLOInformation SRV-HP-A
    Lists iLO firmware information for server SRV-HP-A
    "SRV-HP-A", "SRV-HP-B", "SRV-HP-C" | Get-HPiLOInformation
    Lists iLO firmware information for three servers
    [Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position = 1)][string]$Computername=$env:computername


        if ($pscmdlet.ShouldProcess("Retrieve iLO information from server " +$Computername)){
            $MpFirmwares =  Get-WmiObject -Computername $ComputerName -Namespace root\hpq -Query "select * from HP_MPFirmware"
            ForEach ($fw in $MpFirmwares){
                $Mp = Get-WmiObject -Computername $ComputerName -Namespace root\hpq -Query ("ASSOCIATORS OF {HP_MPFirmware.InstanceID='" + $fw.InstanceID + "'} WHERE AssocClass=HP_MPInstalledFirmwareIdentity")

                $OutObject = New-Object System.Object
                $OutObject | Add-Member -type NoteProperty -name ComputerName -value $ComputerName
                $OutObject | Add-Member -type NoteProperty -name ControllerName -value $fw.Name

                Switch ($Mp.HealthState){
                    5 {$stat = "OK"; break}
                    10 {$stat = "Degraded/Warning"; break}
                    20 {$stat = "Major Failure"; break}
                    default {$stat = "Unknown"}
                $OutObject | Add-Member -type NoteProperty -name HealthState -value $stat

                $OutObject | Add-Member -type NoteProperty -name UniqueIdentifier -value $Mp.UniqueIdentifier.Trim()
                $OutObject | Add-Member -type NoteProperty -name Hostname -value $Mp.Hostname
                $OutObject | Add-Member -type NoteProperty -name IPAddress -value $Mp.IPAddress

                Switch ($Mp.NICCondition){
                    2 {$stat = "OK"; break}
                    3 {$stat = "Disabled"; break}
                    4 {$stat = "Not in use"; break}
                    5 {$stat = "Disconnected"; break}
                    6 {$stat = "Failed"; break}
                    default {$stat = "Unknown"}
                $OutObject | Add-Member -type NoteProperty -name NICCondition -value $stat

                $OutObject | Add-Member -type NoteProperty -name FirmwareVersion -value $fw.VersionString
                $OutObject | Add-Member -type NoteProperty -name ReleaseDate -value ($fw.ConvertToDateTime($fw.ReleaseDate))

                Write-Output $OutObject

Download hpilo.ps1 here

Example of the output

PS C:\Windows\system32> Import-Module C:\MyPath\hpilo.ps1
PS C:\Windows\system32> Get-HPiLOInformation SRV-HP-A

ComputerName : SRV-HP-A
ControllerName : Integrated Lights Out 4 (iLO4)
HealthState : OK
UniqueIdentifier : ILOCZ123456AB
Hostname : SRV-HP-A-ILO
IPAddress :
NICCondition : OK
FirmwareVersion : 1.40
ReleaseDate : 2014-01-14 02:00:00

PS C:\Windows\system32
Categories: Computing, Windows

Gather HP server disk information using PowerShell

2013-05-08 5 comments

I frequently find a need to quickly gather information about the hard drives in my HP Proliant servers. Sometimes I just need to find the model numbers but sometimes I also need information such as serial numbers and firmware versions.
This information is useful when HP releases an urgent firmware upgrade advisory or launches a disk replacement program of which both seem to be quite common lately.

All the needed information is available through WMI on servers that have the HP Insight Management WBEM Providers installed.

Namespace: root\hpq
Classes: HPSA_DiskDrive and it’s associated classes

I wrote the following Powershell function that creates a command that gathers information from servers and returns them as an object that can be sorted, filtered and exported the way you need just as any powershell object.

function Get-HPArrayDisks
    Retrieves physical hard disk information for HP servers.

    The Get-HPArrayDisks function works through WMI and requires
    that the HP Insight Management WBEM Providers are installed on
    the server that is being quiered.

    .PARAMETER Computername
    The HP server for which the disks should be listed.
    This parameter is optional and if the parameter isn't specified
    the command defaults to local machine.
    First positional parameter.

    Lists physical disk information for the local machine

    Get-HPArrayDisks SRV-HP-A
    Lists physical disk information for server SRV-HP-A

    "SRV-HP-A", "SRV-HP-B", "SRV-HP-C" | Get-HPArrayDisks
    Lists physical disk information for three servers

    [Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position = 1)][string]$Computername=$env:computername


        if ($pscmdlet.ShouldProcess("List disks on server " +$Computername)){
            $diskdrives =  Get-WmiObject -Computername $ComputerName -Namespace root\hpq -Query "select * from HPSA_DiskDrive"
            ForEach ($disk in $diskdrives){
                $OutObject = New-Object System.Object
                $OutObject | Add-Member -type NoteProperty -name ComputerName -value $ComputerName
                $OutObject | Add-Member -type NoteProperty -name Slot -value $disk.ElementName
                $OutObject | Add-Member -type NoteProperty -name Interface -value $disk.Description
                $OutObject | Add-Member -type NoteProperty -name RotationalSpeed -value $disk.DriveRotationalSpeed

                $drivePhys = Get-WmiObject -Computername $ComputerName -Namespace root\hpq -Query ("ASSOCIATORS OF {HPSA_DiskDrive.CreationClassName='HPSA_DiskDrive',DeviceID='" + $disk.DeviceID + "',SystemCreationClassName='" + $disk.SystemCreationClassName + "',SystemName='" + $disk.SystemName + "'} WHERE AssocClass=HPSA_DiskPhysicalPackageDiskDrive")
                $OutObject | Add-Member -type NoteProperty -name Model -value $drivePhys.Model
                $OutObject | Add-Member -type NoteProperty -name SerialNumber -value $drivePhys.SerialNumber.trim()

                $driveFW = Get-WmiObject -Computername $ComputerName -Namespace root\hpq -Query ("ASSOCIATORS OF {HPSA_DiskDrive.CreationClassName='HPSA_DiskDrive',DeviceID='" + $disk.DeviceID + "',SystemCreationClassName='" + $disk.SystemCreationClassName + "',SystemName='" + $disk.SystemName + "'} WHERE AssocClass=HPSA_DiskDriveDiskDriveFirmware")
                $OutObject | Add-Member -type NoteProperty -name FirmwareVersion -value $driveFW.VersionString.trim()

                $driveStorage = Get-WmiObject -Computername $ComputerName -Namespace root\hpq -Query ("ASSOCIATORS OF {HPSA_DiskDrive.CreationClassName='HPSA_DiskDrive',DeviceID='" + $disk.DeviceID + "',SystemCreationClassName='" + $disk.SystemCreationClassName + "',SystemName='" + $disk.SystemName + "'} WHERE AssocClass=HPSA_DiskDriveStorageExtent")
                $OutObject | Add-Member -type NoteProperty -name SizeInGigabytes -value ([math]::round(($driveStorage.BlockSize * $driveStorage.NumberOfBlocks) / 1000000000))
                $OutObject | Add-Member -type NoteProperty -name PowerOnHours -value $driveStorage.TotalPowerOnHours

                Write-Output $OutObject

Download hpdisks.ps1 and import it in your powershell session using the command

Import-Module hpdisks.ps1

When you have imported the module you can get help about it’s command just like you would with any other powershell command.

Get-Help Get-HPArrayDisks -detailed

Whatever you pipe into the Get-HPArrayDisks command will be treated as a server name and it will attempt to gather HP disk information from the server.
You can pipe names from the commnd line, from other commands or from text files using the Get-Contents command. Some quick examples:

Get-HPArrayDisks myservername

"myserver1","myserver2" | Get-HPArrayDisks | ft computername, slot, model, serialnumber

Get-Content allmyservers.txt | Sort-Object | Get-HPArrayDisks | Export-Csv -path myserverdisks.csv -NoTypeInformation

You may also want to read
Gather HP iLO information using PowerShell

Categories: Computing, Windows

Using Rotary Encoders with Arduino

2012-11-01 8 comments

Rot enc


There seems to be a lot of confusion among Arduino beginners about how rotary encoders work and how you best use them with Arduino.

I will try to explain a little bit and show some examples to get you started. The example circuit and the code should be enough to get you started if you don’t want to read the other mumbo jumbo.

What is a rotary encoder?

A rotary encoder is electromechanical component with a shaft. Shaft rotation is recorded and converted to electrical pulses that tell in which direction the shaft is rotating. The shaft has unlimited 360 degree rotation.

A rotary encoder can tell you:

  • That the shaft is rotating
  • How much it is rotating
  • In which direction it is rotating

A rotary encoder can NOT tell you:

  • The position or the orientation of the shaft (knobs with indicators are useless)


I’m not going to get into the inner workings of rotary encoders but let’s mention how a simple rotary encoder commuinicate.

There are two output pins (called A and B) that are used to for reading rotation and a third pin that normally connects to GND.
When you turn the encoder, switches inside the encoder opens and closes to turn the outputs HIGH or LOW.

The rotary encoders “speak” gray code which means that the encoder pins are cycling through a predetermined pattern of HIGH and LOW signals.
When you turn the shaft of the encoder clockwise the signal on the encoder pins loops through a pattern like this:

0 0
1 0
1 1
0 1

If we can detect that the pins are changing we know that the shaft is rotating. How often the pulses change tells us how fast it’s turning. How many times it has changed tells us how far it has been turned etc.
When you turn the shaft counter-clockwise it loops through the sequence backwards and that’s how we know in which direction it’s rotating.
Most shaft encoders have fixed stops or “stability positions” so that the shaft stops or clicks at every 0pen-0pen or closed-closed position or both.
Your datasheet will reveal which type you have. There will be a pulse diagram in of the pulses at clockwise operation that looks something like one of the below pictures. The dotted lines indicate shaft stops.

Stops at every closed position

Stops at both closed and open position


Cheap encoders are known to cause a lot of contact bounce so you will need to debounce the signal from the encoders. We will use interrupts on the Arduino for detecting encoder rotation so it’s easier to debounce the encoder with hardware and get nice clean pulses than to try and debounce using software delay.

I have tried some variants and settled with 47nF ceramic capacitors and Schmitt Triggers on the outputs. I have also connected the shield on my encoders to ground through another 47nF ceramic capacitor.

Example circuit

This is how to debounce and connect an encoder to the Arduino:

Example circuit with debounced rotary encoder connected to Arduino

I connect encoder output A to D2 on the Arduino because D2 is an interrupt pin (interrupt 0) which we will use to detect rotation.
Output B can go to any digital pin. I use pin 4 in the example.

If you want to connect another encoder to the same Arduino, use pin D3 for output A on that encoder and enable interrupt 1.

I you have an Arduino Mega you can use four additional interrupts on pins D18, D19, D20, D21.

Arduino sketch

This is an example that uses one interrupt to detect rotation on a rotary encoder which only stops when it’s in closed position.
If you have an encoder that stops at both closed and open you will have to turn your encoder two clicks to register a turn. Correct this by changing the interrupt type from RISING to CHANGE sol that an interrupt is triggered on every change.

int pinA = 2; //Encoder pin A connects to interrupt 0 (D2)
int pinB = 4; //Encoder pin B connects to D4
int iValue = 0; //A variable that will be increased or decreased
                //when we turn the encoder

void setup() {
  pinMode(pinA, INPUT);  
  pinMode(pinB, INPUT);
  // Enable interrupt on encoder pin A
  // Trigger at RISING if your encoder stops (clicks) only at high pulse
  // Trigger at CHANGE if your encoder stops (clicks) at both high and low
  // positions or if it has no stops
  attachInterrupt(0, encoderClick, RISING);

void loop() {
  // continuously print the value to see how it changes

void encoderClick(){
  // encoder must have turned one click because interrupt 0 was triggered
  // read value from both encoder pins
  int valA = digitalRead(pinA);
  int valB = digitalRead(pinB);
  // compare pins to determine in which direction encoder was turned
  if (valA != valB){
      // pinA just changed but pinB had not yet changed
      // Direction must be clockwise if A changes before B
      // pinA just changed and pinB had already done so.
      // Direction must be counter-clockwise if B changes before A

Download the sketch here

Categories: Arduino

Arduino based remote control for electric door

2012-10-14 4 comments

I recently built a replacement remote control system for an electric garage door after the old remote had given up for the babillionth time.
I’m sure the old one would have been possible to repair once again but it was 35 years old and not much fun.

Old receiver and transmitter

Pictures from left to right:

  1. The front of the old receiver with an indoor open/close button.
  2. Inside of the receiver. Nice and clean and very analog electronic fossil.
  3. The guts from the old transmitter. The case that this used to sit in was removed and reused for the new transmitter.
  4. Backside of the old receiver. The label reads “Chambron Radio Control Model C-2216”. There’s also a letter to Mr. Serviceman.
    Who knows anything about this thing?

The motor to the door is controlled by shorting two cables and thereby closing a circuit. A short pulse is enough to start the motor. If the door is closed it will open. If it’s already open, the door will close. If the motor is moving when the pulse arrives it will change direction.

The new solution

433MHz transmitter (left) and receiver (right)

I wanted a new solution that would be a bit more secure and hopefully have the same range as the old one had and I wanted to base it on an ATmega328P with an Arduino core and some really cheap 433Mhz ASK modulated RX/TX modules that can be found everywhere on eBay.

I have used these RF modules before and know they have a decent range and can penetrate doors, windows and even walls in some cases. They are also easy to interface with using the VirtualWire library for Arduino. The downside is that they only work in one direction.

Even if it wasn’t really necessary in this case I wanted to implement some kind of security or obfuscation of the commands sent over the air. This is something of a challenge with a system that only communicates in one direction since it’s not possible to use a challenge/response based protocol and the Arduino doesn’t have storage enough to store predefined passcode sequenced.
This was what I wanted:

  • Use one time passcodes so that simple sniffing wouldn’t be enough to grant access forever
  • Not have to store the passcode sequence in advance
  • Generate passcodes that don’t seem to follow a clear pattern to complicate analysis of the commands

The protocol I ended up with works like this:

  • The receiver and transmitter are paired to use a sequence of numbers starting at a large random number that is decided by the transmitter. The number is sent to the receiver during the pairing process which more or less is a one time operation.
  • When the transmitter wants to send a open/close command to the door it increases the number by one and hashes the number using MD5
  • The receiver knows which number it expects and when it receives a hash from the transmitter it hashes the expected number and compares the resulting hash with the received hash. If the hash is correct it sends a pulse to the door so that it will be opened or closed. The next expected passcode is simply the last passcode +1.
  • If the hashes didn’t match it increases the expected number one by one and creates new hashes to compare with up to fifty times to allow for missed passcodes that might have been sent when the transmitter was out of range or similar. This gives us a sliding window of 50 valid passcodes. As soon as a matching passcode is found, the receiver is synced with the number received and a pulse is sent to the door.
  • The current numbers are stored in the ATmega EEPROM in both transmitter and receiver.


The new transmitter and receiver waiting for installation

I’m using ATmega328P (DIP-28) for both transmitter and receiver. I would probably have used an ATtiny for the transmitter to try and reduce size if it wasn’t for the MD5 library that adds a whopping 12KB to the  binary sketch size making the total sizes around 18KB for both receiver and transmitter.


Since I couldn’t find a suitable pocket size case for the transmitter and a good matching push button I reused the old one. The green LED to the left of the battery pack was added for troubleshooting reasons during installation but I never drilled a hole in the case for that LED due to lack of space.
The button at the top of the PCB is the open/close button. There’s another button on the back side of the PCB that is used during pairing.
The blue cable that is routed from the RF module and around the PCB is the antenna which is a piece of wire that is cut to a quarter of the wavelength equaling 17.3cm.
34.5cm or 69.1cm would also have been OK if there was space enough in the box but looping the antenna round and round in several loops close to each other would not do much good.
The new transmitter resting in the case from the old transmitter (lid open)
The ATmega in the transmitter is running on the internal oscillator at 8MHz to allow the supply voltage to drop to 3.0V. The 16MHz external crystal in the picture is still there but was only used during prototyping. I’m also disabling brownout detection on the ATmega for maximum power savings during power down. The fuses I’m using for setting the clock source to internal oscillator @ 8MHz and disable brownout detection are:
Low Fuses: E2
High Fuses: DA
Extended Fuses: 07

The board definition I put in boards.txt is:

##############################################################, 8MHz Internal OSC, BOD disabled

This configuration makes it possible to use a bootloader if desired but I upload the sketch to the chip using an ISP programmer so I don’t have a need for a bootloader in this case.
For other applications using 8MHz internal oscillator, I have always used the ATmegaBOOT bootloader because I’ve not had any luck compiling the Optiboot for this. If someone has a Makefile that is generating a working Optiboot image for 8MHz internal, please let me know in the comments.


I got rid of the old receiver case because it was made of thick metal and I was afraid that it could affect the reception in a negative way but it would probably have been OK due to the external antenna. There are plenty of boxes suitable for the transmitter but I settled with a minimal, cheap plastic box for electric installations. I should have picked a bigger one because this was a pretty tight fit.
The connector on the right side is the DC plug for the power supply.
To the left is an LED that flashes during pairing and is lit during reception of data. The button on the same side as the LED is the pairing button.

The antenna which is rolled up outside the box in this picture should normally be stretched out. It consists of 69.1 cm of wire soldered to the antenna connection on the RF module.

The new receiver box with lid, sides and terminals removed

Motor connection

I send the pulses for the motor by running the two wires from the motor through a relay inside the receiver. I pull the relay for a short time using an output on the Arduino whenever a matching hash has been received.
I also added an external momentary push button mounted on the wall that shorts the same two wires so that the door can be opened and closed from the inside without using the remote control.

Receiver installed in garage with indoor open/close momentary button attached


How well does this solution work?


50 metres (with the transmitter in a car, new batteries in the transmitter and the receive inside the closed garage door)
This is more than enough and just as good as the old system ever was.

Power consumption: Receiver

Approx. 30mA @ 5V
I haven’t done anything to reduce the power consumption on the receiver since it will be powered by a DC adapter.

Power consumption: Transmitter

The transmitter runs straight from three AA batteries that should (until depleted) keep the voltage over  3.0V at all times which is what the RF transmitter module needs to work properly. The maximum 4.5V is also perfectly fine for both RF module and ATmega.
Current during transmission (basically as long as the button is pressed) is ~25mA @ 4.5V
During powerdown which is the normal condition when no button is pressed, the current drops to 0.1uA
This is as low as it gets with an ATmega328P and to achieve this brownout detection has to be disabled.

Possible future improvements

  • Add possibility to use more than one transmitter.
  • Prevent brute force attacks by adding growing delays after incorrect codes
  • Etch custom PCBs, possibly using SMT components for transmitter
  • Add more decoupling capacitors, at least for the receiver.
  • Shrink the transmitter by creating better and smaller case and PCB for the transmitter
  • Fix bugs
  • Well… let’s see how it works



Download both sketches and all libraries in one zip file: Code


The schemattics for both units in PNG format: Transmitter and Receiver


Uni-T UT61D for Linux

2012-05-10 8 comments


I recenetly purchased a digital multimeter called Uni-T UT61D.
It’s a fairly nice model with decent precision and a build quality that feels good. It’s auto-ranging and measures a little bit of everything. It’s a real improvement to my old basic device and this one also has auto power-off which was a requirement for my new DMM. I’m so tired of putting new batteries in the old one whenever I forget to turn it off (which is all the time). It’s also good to have access to two DMM’s at the same time.

The UT61D has also got a serial interface that connects the DMM to a computer via RS232 or USB. This seemed like a cool feature but of course there were no drivers or applications included for Linux so I started to scan the web for solutions. I could have used a Windows computer but where’s the challenge in that?
Some guys have figured out and written tools for the UT61A, UT61B, UT61C & UT61E which are similar but none seemed to have done much work with the 61D.

he2325u printing raw data from the UT61D

he2325u printing raw data from the UT61D


I could find one driver that after some tweaking extracted data over USB. It was the he2325u by Rainer Wetzel.
His package also included a parser and a plotter that could be used to process the raw data coming out of the he2325u software.
They didn’t work with the 61D because they were written for 61E which has an entirely different protocol.

However the protocol for the B, C, D models is identical. I found a few apps that should work with these models but they were all using RS232 and not the USB HID connection that I wanted to use.


After going through my options I decided to modify one of them so that instead of reading data from RS232 it would read from stdin so that I could pipe data from he2325u to it and use it as a pure parser. The one I settled with was a perl script called dmmut61b which was written by H.P. Stroebel. It had a nice feature set and was easy to modify. Mr Stroebel was also helpful during later troubleshooting because unfortunately it turned out that I was loosing some information somewhere and in the end that turned out to be because the he2325u software wasn’t fully compatible with the B,C,D models protocol. It was masking out bits that contained the micro prefix for μF and μA so they turned up like F and A instead.
With that fixed it finally worked as I wanted.

dmmut61bcd parsing data from he2325u

dmmut61bcd parsing data from he2325u

I have collected modified tools, scripts and source code in an archive in case anyone else needs it:
The he2325u binary in that archive is my modified version that is working well on my 32-bit Ubuntu 12.04 setup.


Protocols documented by Henrik Haftmann
dmm61b by H.P. Stroebel
he2325u by Rainer Wetzel
Uni-Trend documentation
How to get the USB PID device running in Linux

Categories: Equipment, Linux, Software

Using USBtinyISP with homebrew Arduinos in Linux

2011-11-22 5 comments

I had some problems getting my cheap eBay USBtinyISP programmer clone to work in Ubuntu so I thought I’d share some hints or at least write them down for the next time I need them.
Some of the information in this post applies only to Linux systems and I haven’t used it in Windows so I don’t know how that works.

Cheap USBtinyISP clone connected to a breadboard "Arduino"

Cheap USBtinyISP clone connected to a breadboard “Arduino”


If you’re going to burn an Arduino bootloader on a standalone chip for example on a breadboard you normally don’t have any ISP headers available and therefore have to connect the wires by yourself. It’s of course possible to put the chip into a real Arduino board and plug into the ICSP header on the Arduino but I don’t like to jerk chips in and out of my Arduino sockets all the time because it’s just a matter of time before something breaks.
I’m going to assume we’re using a breadboard to hook up the chip being programmed in this example.

It doesn’t matter which of the ICSP connectors you choose to connect to your breadboard. None of them are suitable for breadboard connection anyway since they are both 2-row connectors. There are ICSP breakout boards available that make them easier to use on breadboards but we’re not going to use one this time.
The fastest and cheapest (and probably ugliest) way is to simply connect jumper wires from the ICSP connector to the breadboard.

This is the 6-pin ICSP connector pictured from above so you will have to think a bit when you look at the connector from the bottom side where you insert the jumper wires.

6-pin ICSP connector pinout

6-pin ICSP connector pinout

The 10-pin connectors are a bit different but they have these six connectors too and they are all we need. Google up a picture of the 10-pin connector if you need to use that.

The ICSP connectors are wired to the SPI pins on the ATmega. Connect the six pins according to the following table:

Pin Connect to
MISO  ATmega328 Pin 18 (Arduino D12)
VCC 5V on your board
SCK ATmega328 pin 19 (Arduino D13)
MOSI ATmega328 pin 17 (Arduino D11)
RST ATmega328 pin 1 (directly to pin 1 without 0.1 uF cap)
GND GND on your board

In addition to this the ATmega needs to be hooked up to Vcc and GND and needs an external 16MHz crystal.
The programmers often has a jumper for supply power. With the jumper installed the programmer will supply power to your circuit so you don’t need to install an external power source.

I ran into problems whenever I had something else connected to any of the four SPI pins. For example I’m used to having a led connected immediately to D13 for debugging reasons (compatible with the built in LED on Arduino boards). I didn’t get any response from the ATmega when trying to program it or querying  it using avrdude. My guess is that my LED pulled down the the clock pulses. Removing my LED solved the problem anyway. This obviously isn’t a problem with the built in LED on the Arduino board and I don’t know exactly how they hooked it up but I’m pretty sure they didn’t connect it right onto D13 so if anyone has a recommendation or knows the trick that allows these pins to be used for other things and still be able to program the chip, post it in the comments.

Starting up

It’s now time to connect the programmer to the USB port, fire up the Arduino IDE and get burning.

The USBtinyISP will not show up as a serial port in the Arduino IDE when you connect it and it can not be used to send and receive serial data so don’t bother.

Burning bootloaders

You could try burning a bootloader from the Arduino IDE at this point by selecting a board from the boards menu and selecting Burn Bootloader -> w/ USBtinyISP.
Unless you’re going to use the chip on a Uno board or with some ATmega8U2 based USB-adapter you most likely want to choose Duemilanove or Nano w/ ATmega328. That way you can use an FTDI adapter or cable to upload sketches to it later.

Uploading sketches

You can also use the programmer as an interface for uploading Arduino sketches but that is done without utilizing an Arduino bootloader and you won’t be able to upload any more sketches to that chip using an FTDI or similar interface until you burn a new Arduino bootloader onto the chip.

To create the interface for uploading sketches using the USBtinyISP you need to add the following board definition to arduino/hardware/boards.txt:

############################################################## w/ USBtinyISP


Arduino 1.0 update:
Adding this configuration to boards.txt is no longer necessary in Arduino 1.0 since there is a new built-in function called Upload Using Programmer.

You might have luck in changing the upload speed but in my case increasing it further didn’t affect upload speed at all. My programmer burns at a speed of aproximately 1KByte/s.

After you restart Arduino the new definition will be listed as a board in the Board configuration menu. Select the board named ATmega328 w/ USBtinyISP and hit upload and the sketch will be burned to your chip.

This is useful if you want to do in system programming and don’t have an FTDI cable. When you build a permanent project that you want to be able to re-program later you just add ICSP header pins to your circuit (or a socket for the ATmega).
This is what ISP programmers are made for.

Sketches uploaded using this configuration will overwrite whatever Arduino bootloader you have on your chip and can therefore use all 32768 bytes instead of the normal 32256 bytes (UNO) or 30720 bytes (Duemilanove). That doesn’t sound like much and it isn’t all that much either but if you would use a smaller chip than the ATmega328 with let’s say 4KB of flash those bytes make up a big percentage of the total size.

Linux Permissions

To avoid having to run the Arduino IDE or avrdude as root you need to grant users the right to use the device. This is accomplished by creating a udev rule. This is how it’s done in Ubuntu:

Create the file /etc/udev/rules.d/usbtinyisp.rules containing this single row:
SUBSYSTEM==”usb”, SYSFS{idVendor}==”1781″, SYSFS{idProduct}==”0c9f”, GROUP=”adm”, MODE=”0666″

Restart udev using ‘restart udev’ after saving the file.

Ubunti 12.04 update:
After upgrading to Ubuntu 12.04 LTS the SYSFS keyword didn’t work for me anymore due to some change in udev. The following rule works after the upgrade:
SUBSYSTEM==”usb”, ATTR{idVendor}==”1781″, ATTR{idProduct}==”0c9f”, GROUP=”adm”, MODE=”0666″


Use avrdude to diagnose any errors.
To verify the connection between computer – programmer – microcontroller run the following command:

‘avrdude -c usbtiny -p m328p’

The output from the command should tell whether your setup works or give a clue to why it doesn’t. These are some examples with explanations:

avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.01s
avrdude: Device signature = 0x1e950f
avrdude: safemode: Fuses OK
avrdude done. Thank you.
This is what you’re aiming for. It’s a good respons that means you just talked to the ATmega and it’s begging to be programmed.

avrdude: Error: Could not find USBtiny device (0x1781/0xc9f)
Can’t find the USBtinyISP at all. Is it connected properly?

avrdude: initialization failed, rc=-1
Double check connections and try again, or use -F to override
this check.
Avrdude managed to talk to the USBtinyISP but could not detect your chip. Check the connections to your ATmega again.

avrdude: error: usbtiny_transmit: error sending control message: Operation not permitted
In Linux this means  that you probably need more permissions. Read the Linux Permissions section above.

For the lazy or frequent burner

If you don’t want to bother about putting ICSP headers on every single stripboard but still want to be able to program your chips with ease, build one of these:

USBtinyISP with ZIFduino

USBtinyISP with ZIFduino

This adapter is just a board with a ZIF-socket, 6-pin ICSP header and a fixed 16MHz crystal.
Perfect for bootloading chips or  reprogramming chips used in permanent installations.
16 MHz chips that is…

Categories: Arduino

Get every new post delivered to your Inbox.