lego ir 4channel transmiter with mixer

hello guy's
just finish to test my diy controller for lego power functions
it has 4channels reversable and 2 mixer. it's an rc style radio transmitter

there are many library which do the dirty job to translate some software line into a real comunication with the receiver, but i decided to work it out all in a sketch

i'm not a very skilled programmer so maybe you'll find some newbie errors but any way it work very well!!

here is the code i want to share

Enjoy
Salvo

/*
  lego ir speed remote control arduino

 futures:
 4 tx channel (a, b, c, d)

 reverse a,b,c,d
 mixer   a+b  c+d


 The circuit:
 * channel A to analog input 0
 * channel B to analog input 1
 * channel c to analog input 2
 * channel d to analog input 3
 * pin 4 mixer a
 * pin 5 mixer b
 * pin 6 ir transistor input (can't supply the ir diode directly from arduino!!!)
 * pin 7 a reverse
 * pin 8 b reverse
 * pin 9 c reverse
 * pin 10 d reverse



 */


const int mixa = 4;
const int mixb = 5;
const int ir = 6;
const int reva = 7;
const int revb = 8;
const int revc = 9;
const int revd = 10;



int nibble1;
int nibble2;
int nibble3;
int nibble4;
int nibble5;
int nibble6;
int nibble7;
int nibble8;
int nibble;
int pota ;
int potb ;
int potc ;
int potd ;
int ch1;
int ch2;
int ch3;
int ch4;



void setup()
{
  Serial.begin(9600);
  pinMode(ir, OUTPUT);
  pinMode (reva, INPUT_PULLUP);
  pinMode (revb, INPUT_PULLUP);
  pinMode (revc, INPUT_PULLUP);
  pinMode (revd, INPUT_PULLUP);
  pinMode (mixa, INPUT_PULLUP);
  pinMode (mixb, INPUT_PULLUP);

}

void loop()
{
  get_a();
  get_b();
  
  if (digitalRead(mixa) == LOW) {
    mixer_a();
  }
  
   if (digitalRead(mixb) == LOW) {
    mixer_b();
  }

  if (digitalRead (reva) == HIGH)
  {
    reverse_a();

  }

  if (digitalRead (revb) == HIGH)
  {
    reverse_b();
  }

  if (digitalRead (revc) == HIGH)
  {
    reverse_c();
  }

  if (digitalRead (revd) == HIGH)
  {
    reverse_d();

  }

  

  
  mapping_a();
  mapping_b ();
  nibble1 = 4 |1 ;
  nibble2 = pota;
  nibble3 = potb;
  nibble4 = (15 ^ nibble1 ^ nibble2 ^ nibble3);
  nibble5 = 4 | 0;
  nibble6 = potc;
  nibble7 = potd;
  nibble8 = (15 ^ nibble5 ^ nibble6 ^ nibble7);
  send_message();


}

void get_a()
{
  pota = analogRead(A0) / 58;
  potb = analogRead(A1) / 58;

  if (pota < 1) {
    pota = 1;
  }

  if (pota > 15) {
    pota = 15;
  }
  if (potb < 1) {
    potb = 1;
  }
  if (potb > 15) {
    potb = 15;
  }
}

void get_b() {
  potc = analogRead(A2) / 58;
  potd = analogRead(A3) / 58;

  if (potc < 1) {
    potc = 1;
  }

  if (potc > 15) {
    potc = 15;
  }
  if (potd < 1) {
    potd = 1;
  }
  if (potd > 15) {
    potd = 15;
  }
}

void reverse_a ()
{
  if (pota < 8) {
    pota = 8 + (8 - pota);
  }
  else
  {
    if (pota > 8)
    {
      pota = 8 - (pota - 8);
    }
  }

}


void reverse_b ()
{
  if (potb < 8)
  {
    potb = 8 + (8 - potb);
  }
  else
  {
    if (potb > 8)
    {
      potb = 8 - (potb - 8);
    }
  }
}
void reverse_c ()
{
  if (potc < 8) {
    potc = 8 + (8 - potc);
  }
  else
  {
    if (potc > 8)
    {
      potc = 8 - (potc - 8);
    }
  }

}

void reverse_d ()
{
  if (potd < 8) {
    potd = 8 + (8 - potd);
  }
  else
  {
    if (potd > 8)
    {
      potd = 8 - (potd - 8);
    }
  }

}
void mixer_a ()
{
 
     int delta_b;
     
     delta_b = potb  - 8;
     
     
  ch1 = pota + delta_b;
  ch2 = pota - delta_b;
  
  if (ch1 < 1)
  {ch1 = 1;}
  if(ch2 < 1)
  {ch2 = 1;}
  if (ch1 > 15)
  {ch1 = 15;}
 if (ch2 > 15)
  {ch2 = 15;}
  
pota= ch1;
potb= ch2;
  
}

void mixer_b ()
{
 
     int delta_d;
     
     delta_d = potd  - 8;
     
     
  ch3 = potc + delta_d;
  ch4 = potc - delta_d;
  
  if (ch3 < 1)
  {ch3 = 1;}
  if(ch4 < 1)
  {ch4 = 1;}
  if (ch3 > 15)
  {ch3 = 15;}
 if (ch4 > 15)
  {ch4 = 15;}
  
potc= ch3;
potd= ch4;
  
}

void mapping_a ()
{

  //pot max = 993, pot min = 125


  if (pota < 8) {
    pota = 8 - pota;
  }
  if (pota > 8) {
    pota = (8 - (pota - 8)) + 8;
  }

  //pot max = 993, pot min = 125

  if (potb < 8) {
    potb = 8 - potb;
  }
  if (potb > 8) {
    potb = (8 - (potb - 8)) + 8;
  }
}


void mapping_b ()
{

  //pot max = 993, pot min = 125


  if (potc < 8) {
    potc = 8 - potc;
  }
  if (potc > 8) {
    potc = (8 - (potc - 8)) + 8;
  }

  //pot max = 993, pot min = 125

  if (potd < 8) {
    potd = 8 - potd;
  }
  if (potd > 8) {
    potd = (8 - (potd - 8)) + 8;
  }
}


void ir_mark()
{
  digitalWrite (ir, HIGH);
  delayMicroseconds (10);
  digitalWrite (ir, LOW);
  delayMicroseconds (10);
  digitalWrite (ir, HIGH);
  delayMicroseconds (10);
  digitalWrite (ir, LOW);
  delayMicroseconds (10);
  digitalWrite (ir, HIGH);
  delayMicroseconds (10);
  digitalWrite (ir, LOW);
  delayMicroseconds (10);
  digitalWrite (ir, HIGH);
  delayMicroseconds (10);
  digitalWrite (ir, LOW);
  delayMicroseconds (10);
  digitalWrite (ir, HIGH);
  delayMicroseconds (10);
  digitalWrite (ir, LOW);
  delayMicroseconds (10);
  digitalWrite (ir, HIGH);
  delayMicroseconds (10);
  digitalWrite (ir, LOW);
  delayMicroseconds (10);
}

void nibble_send ()
{
  if (bitRead (nibble, 3) == 0) {
    zero();
  }
  else
  {
    uno();
  }

  if (bitRead (nibble, 2) == 0) {
    zero();
  }
  else
  {
    uno();
  }

  if (bitRead (nibble, 1) == 0) {
    zero();
  }
  else
  {
    uno();
  }

  if (bitRead (nibble, 0) == 0) {
    zero();
  }
  else
  {
    uno();
  }

}

void uno ()
{
  ir_mark();
  delayMicroseconds (555);
}

void zero ()
{
  ir_mark();
  delayMicroseconds (265);
}

void  start_stop () {
  ir_mark();
  delayMicroseconds (1028);

}


void message_a ()
{
  start_stop();
  nibble = nibble1;
  nibble_send();
  nibble = nibble2;
  nibble_send();
  nibble = nibble3;
  nibble_send();
  nibble = nibble4;
  nibble_send();
  start_stop();
}

void message_b ()
{
  start_stop();
  nibble = nibble5;
  nibble_send();
  nibble = nibble6;
  nibble_send();
  nibble = nibble7;
  nibble_send();
  nibble = nibble8;
  nibble_send();
  start_stop();
}

void send_message ()
{
  message_a();
  delay (16);
  message_b();
  delay (16);
  message_a();
  delay (16);
  message_b();
  delay (16);
  message_a();
  delay (16);
  message_b();
  delay (16);
  message_a();
  delay (16);
  message_b();
  delay (16);
  message_a();
  delay (16);
  message_b();


}

Hello all,

The starter of this thread pushed me to process with LEGO Powerfunctions on Trains + the older LEGO RC Trains IR protocol with NO explaination on the internet.
With some tools reverse engineering the bitstreams results in this:

Start = Stop is bitstream 36kHz with 10 bits en pause 1930 mu s
Uno is bitstream 36kHz with 10 bits and pause 280 mu s
Zero is bitstream 36kHz with 10 bits en pause 844 mu s
1 bit has a duty-cycle 16 mu s high and 11,5 mu s low

pauze (common factor 18 ms, dus verhoudingen
Channel actie bitstreams blok-pauze-blok-pauze-blok-pauze-blok)
1 up Start 1111 1111 1110 0001 Stop 3+2+2
1000 0111 1111 1110 1001 1000 4+5+3
1 down Start 1111 1111 1101 0010 Stop 2+2+3
Start 0111 1111 1101 1010 Stop 3+2+3
1 stop Start 0111 1111 1111 1000 Stop 3+2+2
Start 1111 1111 1111 0000 Stop --------
1 horn Start 1111 1111 1100 0011 Stop --------
Start 0111 1111 1100 1011 Stop --------
2 up Start 1110 1111 1110 0010 Stop 2+3+5
Start 0110 1111 1110 1010 Stop --------
2 down Start 1110 1111 1101 0011 Stop 5+3+3
Start 0110 1111 1101 1011 Stop --------
2 stop Start 1110 1111 1111 0001 Stop 5+4+4
Start 0110 1111 1111 1001 Stop --------
2 horn Start 1110 1111 1100 0100 Stop --------
Start 0110 1111 1100 1100 Stop --------
3 up Start 1101 1111 1110 0011 Stop 5+4+2
Start 0101 1111 1110 1011 Stop --------
3 down Start 1101 1111 1101 0100 Stop 3+2+2
Start 0101 1111 1101 1100 Stop --------
3 stop Start 1101 1111 1111 0010 Stop 5+4+5
Start 0101 1111 1111 1010 Stop --------
3 horn Start 1101 1111 1100 0101 Stop --------
Start 0101 1111 1100 1101 Stop --------
4 up Start 1100 1111 1110 0100 Stop 2+3+5
Start 0100 1111 1110 1100 Stop --------
4 down Start 1100 1111 1110 0101 Stop 3+2+3
Start 0100 1111 1110 1101 Stop --------
4 stop Start 0100 1111 1111 1011 Stop 3+2+3
Start 1100 1111 1111 0011 Stop 2+3+3
4 horn Start 1100 1111 1100 0110 Stop --------
Start 0100 1111 1100 1110 Stop --------

(STOP- button keeps repeating when hold, all others (UP, DOWN, HORN) send only once a message

1e nibble: bit1 bit2 bit3 bit4
Kanaal 1 toggle 1 1 1
Kanaal 2 toggle 1 1 0
Kanaal 3 toggle 1 0 1
Kanaal 4 toggle 1 0 0

2e nibble 1 1 1 1

3e nibble 1 1
Up 1 0
Down 0 1
Stop 1 1
Horn 1 0

4e nibble toggle ? ? ? (checksum ???)
Togglebit reverse from 1st nibble bit1

LEGORCtrainIRtryout.ino (6.7 KB)