IR Remote Control programs for the NEC Protocol and Variants.


Author Message
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 3344
Posted: 02:06am 01 Aug 2025      

Updates to include Apple remotes in Rx and Tx programs. See also Apple thread
' "NEC Apple, JVC16 Samsung32 IR code receiver v5.bas"
' tested on PicoMite v6.00.03, & ArmMiteF4 v5.07.02b4, 160MHz
Dim Integer Dev, Key, V, IRpin = MM.Info(PinNo GP1) '<- set your Pico pin
'Dim Integer Dev, Key, V, IRpin = 1 '= PE2 <- set your F4 pin
'Main prog. after Sub

Sub IR_Rx_NEC_V
 'SetPin IRpin, off :
 SetPin IRpin, DIN 'stop interrupt and prepare to get data
 Local float T=Timer, T1, T2, T3, Tmax=300, Z=T+Tmax
 Local Integer n, W=0, Wr=0 'W = LSB first data Word, Wr is MSB first version
 dev = 0 : key = 0 : V = 0 : KN = ""
 T2=Pulsin(IRpin,1)/1000 ': Print "High after first carrier burst ="; T2;"uS"
 T3 = Timer - T - T2

 'Print :Print "Start sequence ="; T3; " mS Low (carrier burst), ", T2; " mS High then 560 uS Low"
 'wait for start sequence to complete then get data pulses
 'Print "Data pulses, 560uS = 0, 3 x 560uS = 1"

 For n=0 To 31
  T1=Pulsin(IRpin,1)
  'Print T1,

  If (T1 < 2000) AND (T1 > 400) Then

    If T1>1000 AND T1<2000 Then 'measure bits, add to word, LSB first into MSB and shifting down
      W=(W>>1) + &H80000000 'add 1 to MSB
      Wr=(Wr<<1) + 1 'add 1 to LSB
     Else
      W=W>>1 'shift down = add 0 (Variant 1 & 3)
      Wr = Wr<<1 'shift up = add 0 (Reverse bit order = Variant 0 & 2)
    EndIf

   Else
    Exit For
  EndIf
 Next

 Print
 'Print "Data as LSB first = &b"; Bin$(W,32), "&h";Hex$(W,8), W AND &Hffff, (W>>16) AND 255, (W>>24) AND 255, 255-((Wm>>24) AND 255);" Byte 4 inverted"
 'Print "Data as MSB first = &b"; Bin$(Wr,32), "&h";Hex$(Wr,8), (Wr>>16), (Wr>>8) AND &Hff, Wr AND 255, 255-(Wr AND 255);" Byte 4 inverted" :Print

 SetPin IRpin, off
'  Print :Print "Start pulse";T2;"mS  ";Bin$(m,32); " Bit order has been reversed so MSB is on the left"

 'Test for Samsung32 Variant
 If ((W AND 255) = ((W>>8) AND 255)) AND (T3 > 3) AND (T3 < 6) AND (V = 0) Then
    V = 4 'Samsung32, LSB first
    dev = W AND &Hff 'get device code
    key = W >> 16  AND &Hff 'get key code
 EndIf

 'Test for JVC16 Variant
 If ((W AND  &Hffff) = 0) AND (T3 > 7) AND (T2 < 11) AND (V = 0) Then
   V = 5 'JVC16 8 bit codes, LSB first
   dev = (W >> 16) AND &Hff 'get device code
   key = (W >> 24) AND &Hff 'get key code
 EndIf

 'Test for Apple Variant
 If ((W AND &hFFFF) = &h87EE) AND (T3 > 7) AND (T3 < 11) AND (V = 0) Then
   V = 6 'Apple IR, LSB first, 16 bit device code = &h87EE after reversing bits
   Dev = (W>>24) AND &Hff 'get ID code (Apple is not standard so Dev is used for ID code
   parity = W >> 16  AND 1 'get parity bit (LSB of 8 bit Key)
   key = W >> 17  AND 127 'get 7 bit Key code
   Print "V =";V, "parity =";parity, "Key =";Key, "ID =";Dev : Print
 EndIf

 'Test for NEC 8 bit Dev code, LSB first Variant (assumes Variant 3)
 'MSB first and LSB first 8 bit Dev code variants (2 & 3) can't be distinguished so test several Keys,
 ' if the Dev code changes instead of the Key code use V = 2 instead of 3
 If (T3 > 7) AND (T3 < 11) AND (V < 4) Then
  If (((255-(W>>24)) AND 255) = ((W>>16) AND 255)) AND (((255-(W>>8)) AND 255) = (W AND 255)) Then
   V = 3 'NEC 8 bit device code, LSB first
   dev = W AND &Hff 'get device code
   key = (W>>16)  AND &Hff 'get key code
    Print "Codes for 8 bit Dev MSB first (V = 2): Dev ="; (Wr>>16) AND 255; "  Key ="; (Wr>>8) AND 255
    Print "Codes for 8 bit Dev LSB first (V = 3): Dev ="; Dev; "  Key ="; Key : Print
  EndIf
 EndIf

 'Test for NEC 16 bit Dev code, Variants 0 & 1
 If (T3 > 7) AND (T3 < 11) AND (V < 2) Then
  If (((255-(W>>24)) AND 255) = ((W>>16) AND 255)) AND  (((255-(W>>8)) AND 255) <> (W AND 255)) Then
    V = 1 'NEC 16 bit device code, LSB first
    dev = W AND &Hffff 'get device code
    key = (W >> 16)  AND &Hff 'get key code
   Else
    V = 0
    dev = (Wr>>16) AND &Hffff 'get device code MSB first
    key = (Wr>>8) AND &Hff 'get key code
  EndIf
 EndIf


'  If V=6 Then
'    Print "W, T3, T2, V, parity, Key, ID",, W AND &hFFFF,T3,T2,V,parity,key,dev
'   Else
'    Print "W, T3, T2, V, Dev, Key",, W AND &hFFFF,T3,T2,V,dev,key
'  EndIf

'  If V<>6 Then Print Dev, Key : Print

 If dev<>0 AND key<>0 Then 'output data

   If V = 6 Then
   Print "ID, Key, V", Hex$(Dev);","; Hex$(Key,2);","; V,
     Select Case Key
      Case 1 : KN$ = "Menu"
      Case 46 : KN$ = "Center, Select"
      Case 3 : KN$ = "Right, Next/Fast-Forward"
      Case 4 : KN$ = "Left, Previous/Rewind"
      Case 5 : KN$ = "Up, Volume +"
      Case 6 : KN$ = "Down, Volume -"
      Case 47 : KN$ = "Play/Pause"
      Case 2 : KN$ = "follows Center/Select or Play/Pause"
      Case Else : KN$ = "QUIT"
     End Select :Print KN$
   EndIf

   If KN$ = "" Then
     Print "Device =";Dev, "Key =";Key, "Variant =";V,
     Input "  Enter the Key name "; KN$
   EndIf

   If (KN$ = "Quit") OR (KN$ = "quit") OR (KN$ = "QUIT") Then
     Print #3, : Print #3,"To use NECsend without the Variant number"
     Print #3,"replace the Device code:- ";Dev;" with:- ";Dev + (V<<16)
     Print #3,"except for Apple (V6) replace ID:- ";Dev;" with:- ";Dev + &h60000 : Print #3,""
     Pause 55
     Close #3
     Print
     List "NEC_Key_Codes.txt"
     End
    Else
     Print #3, Dev;","; Key;","; V; "  '"; KN$
   EndIf

 EndIf

 Pause 300 'prevent re-triggering, Samsung & JVC remotes always send code twice
 SetPin IRpin, INTL, IR_Rx_NEC_V 'prepare for next input

End Sub

'Main
Dim string KN$
Print
Open "NEC_Key_Codes.txt" For append As #3
Pause 55
Print "To save the data (NEC_Key_Codes.txt) press any RC key"
Print " and type Quit at the 'Enter Key name ?' prompt."
Print "Except for Apple (V = 6), use Key = 0 to quit" : Print
Print "Device, Key, Variant, 'Function"
Print #3, "'Device, Key, Variant, 'Function,,NEC_Key_Codes.txt"
Timer = 0
SetPin IRpin, INTL, IR_Rx_NEC_V
Do : Loop

End


' "NECsend with Apple, JVC16 & Samsung32 variants v5.bas"
Dim Integer IRpin = MM.Info(pinno GP0) 'pin 1 for PicoMite
' Dim Integer IRpin = 23 '= PA0 'for ArmMiteF4
Dim Integer dev, key, V
Print "for NEC MMBasic IR command compaitbility (MSB first, 16 bit Dev code) - V = 0, or omit"
Print "for NEC, LSB first, 16 bit Dev code - V = 1 (NEC standard)"
Print "for NEC, MSB fits,t 8 bit Dev code - V = 2 (not really needed)"
Print "for NEC, LSB first, 8 bit Dev code - V = 3 (old NEC standard)"
Print "for Samsung32, LSB first -  V = 4"
Print "for JVC16, LSB first - V = 5"
Print "for Apple, LSB first - V = 6, Dev is the Remote ID No. (byte 4). The actual Dev. code is &hEE, &h87 always"
Print " and the least significant bit of the Key byte is a parity bit over the Key byte and the ID byte."
Print " The ID code of an Apple remote may be changed by -"
Print " 'Press and hold MENU + PLAY/PAUSE on any Apple remote to change (roll) its ID'"
Print
Print "Alternatively those Variants numbers can instead be denoted by adding them to Dev above bit 16"
Print "eg. for S32 (V=4) added to Device &H0005 gives &H40005 or Dev = 262149"
Print :Print

'Measure Device Bitstream startup time.
Dim integer d(1390) : Dim float T, TD
Math Set 13, d() '13uS intervals
T=Timer
Device Bitstream IRpin, 1390, d()
T=Timer-T ':Print T,
TD = T-13*1390/1000-0.1 ': Print "TD ="; TD "mS  Bitstream calibration value"
Erase d(), T

'Main
Do
'Your program here.
Input "Enter Device, Key, Variant (NEC=0 to 3, S32=4, JVC=5, Apple=6) codes "; dev, key, V

NECsend IRpin, dev, key, V
Loop

Sub NECsend IRpin As integer, Dev As integer, Key As integer, V As integer
'For Apple codes (V = 6) Dev is the Remote ID No. 1 byte. The actual Dev. code is &h87EE
 Local Integer word, d(1390), m, n = 1
 Local Float T
 Pin(IRpin) = 0
 Math set 14, d() 'fill array. 13=1000/(38*2) uS - duration of a half cycle @ 38kHz

 If V = 0 Then V = (Dev >> 16) ' if V missing or 0 then look for V in Dev above bit 16
 Dev = Dev And &hFFFF

   If (V=2) Or (V=3) Then Inc Dev, ((255-Dev) << 8)  'convert 8 bit address to 16 if required
   If (V=1) Or (V=3) Then
     key = ((Key And 255) * &H0202020202 And &H010884422010) Mod 1023 ' reverse Key bit order, 8 bit
     dev16 = (((dev And 255) * &H0202020202 And &H010884422010) Mod 1023) << 8 ' reverse Dev bit order, low byte
     dev = dev16 + ((dev >> 8) * &H0202020202 And &H010884422010) Mod 1023  'now reverse high byte & swap bytes
   EndIf
   word = (dev<<16) + (key<<8) + 255-key 'assemble 32 bits for 16 bit Device, Key and inverted Key
  'Print "Word = &b"; Bin$(word,32), "&h"; Hex$(word,8)

'  Samsung32. usage - Sub NECsend IRpin, Dev, Key, 4
  If V = 4 Then
   dev = ((Dev And 255) * &H0202020202 And &H010884422010) Mod 1023 'ensure 8 bits only &
   key = ((Key And 255) * &H0202020202 And &H010884422010) Mod 1023 ' reverse bit order
   word = (dev<<24) + (dev<<16) + (key<<8) + 255-key 'assemble 32 bits
   Device bitstream IRpin, 324, d() 'send 4.5ms start 38kHz
  EndIf

'  JVC16 usage - Sub NECsend IRpin, Dev, Key, 5
  If V=5 Then
    dev = ((dev And 255) * &H0202020202 And &H010884422010) Mod 1023 ' reverse Dev bit order, 8 bit
    key = ((Key And 255) * &H0202020202 And &H010884422010) Mod 1023 ' reverse Key bit order, 8 bit
    word = (dev<<24) + (key<<16)
  EndIf

'  Apple IR. usage - Sub NECsend IRpin, ID, Key, 6 'In this section ID uses the variable Dev
  If V = 6 Then
'   ID = &h4D = 77 - WhiteWizard.  ID = &h59, &h68, &h15 =(89, 104, 21) - Wikipedia
'   press and hold MENU + PLAY/PAUSE on any Apple remote to change (roll) its ID -
'   https://hifiduino.wordpress.com/apple-aluminum-remote/
'   LSB-> &hEE, &h87, Key, ID <-MSB, LSB of Key = parity bit (1=even) for next 15 bits. Key code (7 bits) and ID (8 bits)
   Local Integer Pbit = 1 'Parity bit is 1 if data bits are even
   For n= 0 To 7 : Pbit =  (Pbit + ((Dev>>n) And 1)) And 1 : Next 'find parity bit
   For n= 0 To 6 : Pbit =  (Pbit + ((Key>>n) And 1)) And 1 : Next
   Key = ((Key And 127)<<1) + Pbit 'convert 7 bit Key to 8 bits and add parity bit
   Key = ((Key And 255) * &H0202020202 And &H010884422010) Mod 1023 ' reverse  bit order, 8 bit
   Dev = ((Dev And 255) * &H0202020202 And &H010884422010) Mod 1023 ' reverse  bit order, 8 bit
   word = (30689<<16) + (key<<8) + dev 'assemble 32 bits '16 bit address only. dev = Apple ID No.
  EndIf
'    Print "Word = &b"; Bin$(word,32), "&h"; Hex$(word,8)

  If V <> 4 Then  Device bitstream IRpin, 650, d() 'send 9ms start 38kHz
  T = Timer : n = 1
  For m = 42 To 1386 Step 42  'load data after start sequence, step 42 = 21 cycle IR pulse
   d(m) = (((word>>(32-n)) And 1)*2 + 1) * 560  'convert bits to duration (560uS = 0, 1680uS = 1)
   Inc n 'step to next data bit
  Next

  Do : Loop Until Timer > T+4.5-TD 'mS. Less than 4.5mS due to bitstream initialization time,
' adjust for CPU speed and firmware version.
  If V<>5 Then
    Device bitstream IRpin, 1386, d() 'send NEC data
  EndIf
  If V=5 Then
    Device bitstream IRpin, 698, d() 'send V5 JVC data
    Pause 33
    Device bitstream IRpin, 698, d() 'repeat JVC data
  EndIf

'  Math V_Print d()

  Pin(IRpin) = 0
End Sub
End


Edit.
In recent PicoMite firmware Device Bitstream takes longer to load than older versions.
This affects the pause time between the initial carrier burst and the start of data.
Adjust to get close to 4.5mS. Some devices are very tolerant, others not so much.

Do : Loop Until Timer > T+4.04 'mS. Less than 4.5mS due to bitstream initialization time


To determine the required adjustment on your system use this program.
SetPin gp3, dout
Dim a(999)
Math set 10,a() 'make a 10mS data stream
For n=0 To 9
 T=Timer
 Device bitstream gp3,1000,a()
 T=Timer-T-10
 Print "Bitstream startup time"; T; "mS"
Next

> RUN
Bitstream startup time 0.4710000008mS
Bitstream startup time 0.4790000021mS
Bitstream startup time 0.4640000015mS
Bitstream startup time 0.4649999999mS
Bitstream startup time 0.4609999992mS
Bitstream startup time 0.4510000013mS
Bitstream startup time 0.4539999999mS
Bitstream startup time 0.4629999995mS
Bitstream startup time 0.4599999972mS
Bitstream startup time 0.4619999975mS
>

So to get a 4.5mS pause:-

4.5 - 0.46 = 4.04mS

The size of the array only has a small effect on the time.
I guess this measurement could done be in the program in the setup area to make it self calibrating.

Edit 2.
Auto calibration has been added to the NECsend program.
Edited 2025-08-02 16:22 by phil99

Footnote added 2025-08-05 09:54 by phil99
Noticed a typo in the NECsend Sub.
Sub NECsend IRpin As integer, Dev As integer, Key As integer, V As integer
'For Apple codes (V = 6) Dev is the Remote ID No. 1 byte. The actual Dev. code is &h87EE
Local Integer word, d(1390), m, n = 1
Local Float T
Pin(IRpin) = 0
Math set 14, d() 'fill array. 13=1000/(38*2) uS - duration of a half cycle @ 38kHz

Math set 14, d() should be Math set 13, d()
All the devices I have tolerate this carrier frequency ok so hadn't noticed it.

14uS was a fudge for a previous version of the ArmMiteF4 firmware whose Bitstream command ran a little fast.

Footnote added 2025-08-05 11:40 by phil99
Getting even more forgetful. Changing that changes the length of the starting carrier burst. Once again has no effect on anything I have but for accuracy here are the changes with the new bitstream values in red.
'  Samsung32. usage - Sub NECsend IRpin, Dev, Key, 4
 If V = 4 Then
  dev = ((Dev And 255) * &H0202020202 And &H010884422010) Mod 1023 'ensure 8 bits only &
  key = ((Key And 255) * &H0202020202 And &H010884422010) Mod 1023 ' reverse bit order
  word = (dev<<24) + (dev<<16) + (key<<8) + 255-key 'assemble 32 bits
  Device bitstream IRpin, 350, d() 'send 4.5ms start 38kHz
 EndIf

and
 If V <> 4 Then  Device bitstream IRpin, 700, d() 'send 9ms start 38kHz
 T = Timer : n = 1
 For m = 42 To 1386 Step 42  'load data after start sequence, step 42 = 21 cycle IR pulse
  d(m) = (((word>>(32-n)) And 1)*2 + 1) * 560  'convert bits to duration (560uS = 0, 1680uS = 1)
  Inc n 'step to next data bit
 Next