Last active
January 29, 2025 23:00
-
-
Save chadbaldwin/994b710557bd4eb7b0e6d9c50e6dd938 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$base64 = 'JgB4AQABIJASEhISEjcSEhISEhMRExISEjYSNhITETcRNxE3EzYSNhISEjcRExETERMRExETEhITNhISEjcRNxE3ETcSNhI2EgAE5wABH0kSAAv1AAEgSBEAC/UAASBJEgAL9QABH0kSAAv1AAEgSREAC/UAASBJEgAL9gABH0gSAAv2AAEgSBIAC/UAASBJEgAL9gABH0kRAAv2AAEgSBIAC/UAASBIEwAL9QABH0kSAAv1AAEgSREAC/UAASBJEgAL9gABH0kRAAv1AAEhSBEAC/YAASBIEgAL9QABIEkRAAv2AAEgSBEAC/YAASBIEwAL9QABH0oRAAv1AAEgSBIAC/UAASBJEgAL9QABH0kSAAv1AAEgSBEAC/YAASBIEwAL9QABH0kSAAv1AAEgSBIAC/UAASBJEgAL9QABH0oRAAv1AAEgSREAC/UAASBJEgAL9QABIEkRAAv1AAEgSREAC/UAASFIEgAL9QABH0oRAAv1AAEhSBIADQU=' | |
$output = [pscustomobject]@{ | |
OriginalBase64 = $base64 | |
RawMessage = [System.Convert]::FromBase64String($base64) | |
MessageType = $null | |
RepeatCount = $null | |
MessageLength = $null | |
InitialPulse = $null | |
NECMessage = [uint]::MinValue # 4 byte int to store the 4 NEC message sections | |
NECMessageHexString = $null | |
CleanByteArray = $null | |
CleanBase64String = $null | |
Pulses = @() | |
} | |
<# This intiialization consists of the standard Broadlink header (type, repeat, message length) | |
as well as the initial 9ms/4.5ms pulse #> | |
$output.CleanByteArray = [System.Collections.Generic.List[byte]]@(0x26,0x00,0x48,0x00,0x00,0x01,0x27,0x93) | |
$data = $output.RawMessage | |
$output.MessageType = switch ($data[0]) { | |
0x26 { 'IR' } | |
0xb2 { 'RF 433Mhz' } | |
0xd7 { 'RF 315Mhz' } | |
} | |
if ($output.MessageType -ne 'IR') { throw 'Only IR is supported at this time' } | |
$output.RepeatCount = $data[1] | |
$output.MessageLength = ([int]$data[3] -shl 8)+$data[2] | |
$pulseCount = 0 | |
# Message body index starts at 4 | |
for ($i = 4; $i -lt $data.Length; $i++) { | |
$pulseCount++ | |
# Stop once we reach the end of a standard 32 bit/pulse message (+ final end pulse, which makes it 33) | |
# Anything beyond that _should_ just be repeat codes for holding buttons | |
if ($pulseCount -gt 33) { break } | |
# Marks and spaces can be either 1 or 3 bytes long. If the first byte is 0x00, then it's a 3 byte value | |
$markTime = if ($data[$i] -eq 0x00) { ([int]$data[$i+1] -shl 8)+$data[$i+2]; $i+=2 } else { $data[$i] } | |
$i++ | |
$spaceTime = if ($data[$i] -eq 0x00) { ([int]$data[$i+1] -shl 8)+$data[$i+2]; $i+=2 } else { $data[$i] } | |
# Determine whether the mark and space falls within a valid range | |
$mark = ($markTime -ge 15) -and ($markTime -le 21) # hard code to true? Seems to always fall in this range for valid 32-bit messages | |
$space = switch ($spaceTime) { | |
{ ($spaceTime -ge 15) -and ($spaceTime -le 21) } { $false } | |
{ ($spaceTime -ge 50) -and ($spaceTime -le 58) } { $true } # change to simple if else? Values always fall in either of two ranges | |
default { $null } | |
} | |
$output.Pulses += [PSCustomObject]@{ | |
PulseIndex = $pulseCount | |
PulseType = switch ($pulseCount) { | |
1 { 'Initial' } | |
{ $_ -ge 2 -and $_ -le 9 } { 'Address' } | |
{ $_ -ge 10 -and $_ -le 17 } { 'Address Check' } | |
{ $_ -ge 18 -and $_ -le 25 } { 'Command' } | |
{ $_ -ge 26 -and $_ -le 33 } { 'Command Check' } | |
} | |
Pulse = $mark -and $space | |
MarkTimeRaw = $markTime | |
MarkTimeus = $markTime/32768*1000000 | |
SpaceTimeRaw = $spaceTime | |
SpaceTimeus = $spaceTime/32768*1000000 | |
} | |
# The first pulse is not part of the NEC message, but is used to determine the initial pulse timings | |
# So store that separately and continue to the next pulse | |
if ($pulseCount -eq 1) { | |
$output.InitialPulse = $markTime,$spaceTime | |
continue | |
} | |
<# Build the NEC message by continually shifting it left and adding the new pair results #> | |
$output.NECMessage = $output.NECMessage -shl 1 | |
$output.NECMessage += $mark -and $space | |
<# | |
0x12 and 0x37 are the closest to the proper NEC pulse time of 562.5μs | |
Broadlink uses increments of 1/32768 of a second, which is 2^-15 seconds | |
This means a pulse timing of 0x01 would be: | |
0x01 / 32768 * 1000000 = 30.517578μs | |
Which means: | |
0x12 (18) x 30.517578 = ~549.32μs (ideal is 562.5) | |
0x37 (55) x 30.517578 = ~1678.47μs (ideal is 1687.5) | |
#> | |
$output.CleanByteArray += 0x12uy | |
$output.CleanByteArray += $space ? 0x37uy : 0x12uy | |
} | |
<# uint's are stored little endian, but NEC codes are big endian, so we need to reverse then convert to hex string #> | |
$output.NECMessage = [System.BitConverter]::GetBytes($output.NECMessage) | |
[array]::Reverse($output.NECMessage) | |
$output.NECMessageHexString = "0x$([System.Convert]::ToHexString($output.NECMessage))" | |
<# This final byte array is just the final ending pulse to signify the end of the transmission #> | |
$output.CleanByteArray += 0x12uy,0x00uy,0x0Duy,0x05uy | |
$output.CleanBase64String = [System.Convert]::ToBase64String($output.CleanByteArray) | |
$output.Pulses | ft -AutoSize | |
$output | fl | |
<# Notes: | |
Mark = IR light is on | |
Space = IR light is off | |
I'm still confused on the whole pulse vs mark vs burst terminology. Not sure how to refer to a single "on" vs a pair of "on then off" | |
All timings are based on 562.5μs | |
Logical 0 = 1 burst + 1 space | |
Logical 1 = 1 burst + 3 space | |
The standard NEC transmission is as follows: | |
1. 9ms burst (AGC burst) | |
2. 4.5ms space | |
3. 8 burst+space pairs representing the "Address" of the device (to prevent cross talk) | |
4. Verification step - same 8 pairs above, but inverted | |
5. 8 burst+space pairs representing the "Command" (the action we want to perform) | |
6. Verification step - same 8 pairs above, but inverted | |
7. Final single burst to indicate end of transmission | |
8. If button is being held | |
- TODO - Something like: | |
- At every 108ms interval, send: 9ms burst, 2.25ms space, 562.5μs burst | |
https://www.sbprojects.net/knowledge/ir/nec.php | |
https://cdn.hackaday.io/files/1624456950046880/Documentation.pdf | |
https://github.com/mjg59/python-broadlink/blob/master/protocol.md | |
https://www.reddit.com/r/homeassistant/comments/pl03cj/comment/hc8n0dj/ | |
https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#nec-encoding-diagrams | |
https://exploreembedded.com/wiki/NEC_IR_Remote_Control_Interface_with_8051 | |
#> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment