Skip to content

Instantly share code, notes, and snippets.

@TG9541
Last active August 16, 2023 05:06
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TG9541/40b811c3a611eb02ba9e3693f99222ac to your computer and use it in GitHub Desktop.
Save TG9541/40b811c3a611eb02ba9e3693f99222ac to your computer and use it in GitHub Desktop.
IR Remote Control for an RGBW LED Bulb with STM8 eForth

STM8 eForth: IR Remote Control for an RGBW LED Bulb

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).

image

The rock bottom price of the W28 remote control shows - it's just a bit of plastics with the cheapest film keypad possible.

IR remote code analysis

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.

image

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:

image

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.

Implementation

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment