These days IR remote control is still the cheapest option for consumer electronics. I've long had the idea that Forth is ideal for scripting IR remote-controlled devices and I used the light bulb at my desk to learn how to do that with STM8 eForth. LED lamps of relatively high quality with a combination of white LED and RGB LEDs have a remarkable low price tag: the set below costs no more than a regular white LED lamp. In my experience service life is much better (maybe due to a switched mode power supply with constant DC voltage and µC PWM control). The option for occassional colorful illumination is a bonus.
This writeup covers analyzing light bulb IR remote control codes and using it with STM8 eForth . The code below also is a showcase for the expressive power of STM8 eForth on a cheap 8bit µC (e.g., STM8S103F3P6 w/ 8K Flash and 1K RAM).
The rock bottom price of the W28 remote control shows - it's just a bit of plastics with the cheapest film keypad possible.
IR remotes emmits bursts of IR pulses to transmit pulse or pause width coded data. A burst, often with a 38kHz IR pulse frequency, indicates a "mark". IR receivers are tuned to a specific burst center frequency, e.g., 38kHz, 32.75kHz or 56.8kHz. The most common burst frequency appears to be 38kHz, and that's also what the W28 remote control uses. For best sensitivity it's important to use a matching receiver but a 36.7kHz receiver works well with a 38kHz remote.
I used a rather "dated" IR receiver (SHARP GP1UD272XK, available for a few cents) and a cheap USB logic analyzer with PulseView to analyze the protocol. Pulseview can decode some IR protocols - and when I noticed that the remote uses the "NEC IR protocol" the analysis was over before it began:
The "W28 remote control" uses "0" as the address (regular protocol, i.e. $00 followed by $FF) and a "command - repeat" sequence.
The commands of the remote control are as follows:
W28 Keys | Column 1 | Column 2 | Column 3 | Column 4 |
---|---|---|---|---|
Row 1 | 5 bright | 4 dim | 6 off | 7 on |
Row 2 | 9 red | 8 green | 10 blue | 11 white |
Row 3 | 13 red1 | 12 green1 | 14 blue1 | 15 f-col |
Row 4 | 21 orange | 20 cyan | 22 purple | 23 r-col |
Row 5 | 25 orange1 | 24 petrol | 26 purple1 | 27 flash |
Row 6 | 17 yellow | 16 teal | 18 pink | 19 s-col |
These bulbs don't implement any other codes (other people have the same opinion). I own one light bulb with a similar remote controller without the "W28" mark which uses different codes. Many other Chinese products appear to be using NEC IR codes. Many, like the bulb, ignore the address codes. If you use some other Chinese devise with IR remote control don't be too surprised if the color of your LED bulb changes.
I decided to use a timer to produce the 38kHz carrier with the STM8 TIM1 Capture/Compare PWM function. A rather generic TIM1 "update event" ISR creates the sequence of "mark" and "space" required by the NEC protocol. This "engine" can be easily changed to produce other protocols.
There are two variants of the code: one using TIM1_CH3 (below) and the other TIM1_CH1 (attached, requires setting option byte OPT2
). The code works with any current release of STM8 eForth:
\ "W28" IR remote control with NEC protocol for a Chinese "RGBW" LED lamp
\ using an STM8S Low Density device (e.g. STM8S103)
\ IR LED cathode connected to PC3 (TIM1_CH3)
\res MCU: STM8S103
\res export TIM1_CR1 TIM1_IER TIM1_SR1 TIM1_BKR TIM1_CCER2
\res export TIM1_ARRH TIM1_CCMR3 TIM1_CCR3H INT_TIM1
\ TIM1 bit constants
0 CONSTANT UIE
0 CONSTANT CC3E
7 CONSTANT MOE
#require :NVM
#require ]B!
#require >REL
\ ( carry-flag ) IF with relative addressing
: ]CFIF ( -- ) $24 C, ] >REL ; \ JRNC rel
NVM
VARIABLE mrk \ mark duration in 1/38kHz pulses
VARIABLE spc \ spc duration in 1/38kHz pulses
VARIABLE bcnt \ bit counter
VARIABLE sreg 2 ALLOT \ IR send register for ISR
\ IR-NEC pattern ISR triggered by TIM1 Update Event
:NVM
SAVEC
[ 0 TIM1_SR1 UIE ]B! \ clear interrupt
mrk @ ?DUP IF
[ 1 TIM1_CCER2 CC3E ]B! \ mark: enable 38 kHz PWM
1- mrk !
ELSE
[ 0 TIM1_CCER2 CC3E ]B! \ space: disable 38 kHz PWM
spc @ ?DUP IF
1- spc !
ELSE
bcnt @ ?DUP IF
1- bcnt !
24 mrk ! [ \ 0.65 ms mark
\ rotate right bit pattern - NEC protocol is LSB first
$36 C, sreg 3 + C, \ RRC sreg+3 ; (zero page addressing)
$36 C, sreg 2 + C, \ RRC sreg+2
$36 C, sreg 1 + C, \ RRC sreg+1)
$36 C, sreg C, \ RRC sreg
]CFIF
60 spc ! \ carry flag set: spc 1.6ms
ELSE
19 spc ! \ carry flag clr: spc 0.5ms
THEN
THEN
THEN
THEN
IRET
[ OVERT ( xt ) INT_TIM1 !
\ Init TIM1 for 38kHz ticker interrupt and 50 duty PWM output
: init ( -- )
$60 TIM1_CCMR3 C! \ PWM mode 1
421 TIM1_ARRH 2C! \ 38kHz @ 16MHz HSI
210 TIM1_CCR3H 2C! \ 50% duty cycle
[ 1 TIM1_BKR MOE ]B! \ main output enable
[ 1 TIM1_CCER2 CC3E ]B! \ Capture Compare output enable (CC3E)
[ 1 TIM1_CR1 UIE ]B! \ enable timer
[ 1 TIM1_IER UIE ]B! \ enable timer update interupt
;
\ turn c into MSB, complement LSB
: NECinv ( c - n ) [
\ 256 OVER * SWAP NOT 255 AND OR
$E601 , \ LD A,(1,X)
$6301 , \ CPL (1,X)
$F7 C, \ LD (X),A
] ;
\ send NEC-IR code with W28 remote control timing
: ir ( c -- )
[ 0 TIM1_CR1 UIE ]B! \ disable timer interrupt
0 NECinv sreg ! \ the W28 remote control uses address 0
( c ) NECinv sreg 2+ !
342 mrk ! \ 9ms AGC pulse (first mark)
171 spc ! \ 4.5ms leader pause (first space)
33 bcnt ! \ NEC: 33 mark/space events
[ 1 TIM1_CR1 UIE ]B! \ enable timer interrupt
;
\ wait until the IR code transmission has ended
: irw ( -- )
BEGIN bcnt @ 0= UNTIL
;
RAM
\\ Example:
\ -----------|----------|---------|----------
\ 5 bright | 4 dim | 6 off | 7 on
\ -----------|----------|---------|----------
\ 9 red | 8 green |10 blue |11 white
\ -----------|----------|---------|----------
\ 13 red1 |12 green1 |14 blue1 |15 cf-col
\ -----------|----------|---------|----------
\ 21 red2 |20 green2 |22 blue2 |23 cr-col
\ -----------|----------|---------|----------
\ 25 red3 |24 green3 |26 blue3 |27 flash
\ -----------|----------|---------|----------
\ 17 red4 |16 green4 |18 blue4 |19 cs-col
\ -----------|----------|---------|----------
7 ir \ lamp on
10 ir irw 11 ir \ flash blue, then white
6 ir \ lamp off
A recording from a Forth2020 User-Group meeting on YouTube shows a demo of a slightly modified version of this code for the C0135 relay board. The code is in this gist.