zondag 30 maart 2014

The Channel selection code

Introduction


Coding the channel selection for the Arduino is not really difficult, but has some attention points.

When the Telis 4 remote is idle and no LEDs are blinking, changing the channel can be established with only two steps:

  1. press the select button to have the remote show its current channel
  2. keep pressing the select button until the right channel is selected
But, we should be able to handle the fact that the LEDs are already be blinking when starting a channel selection, so the steps I implemented are:

  1. look at the LEDs and determine the current selected channel
  2. keep pressing the select button until the right channel is selected
With only a few Arduino functions this can be achieved:

Pressing a button


The buttons of the remote are wired to the Digital Out pins 2-5 of the Arduino. Putting a HIGH signal (+5v) on the pin during 100ms will activate the button. I came up with the 100ms by trial and error (50ms will not trigger the button). 

The Arduino code:

#define SOMFY_BUTTON_UP   2
#define SOMFY_BUTTON_DOWN 3
#define SOMFY_BUTTON_MY   4
#define SOMFY_BUTTON_MENU 5

int press_button(int button){

  
  digitalWrite(button, HIGH);
  delay(100);
  digitalWrite(button, LOW);
}


Reading the status of the LEDs


The LEDs of the remote are wired to the Analog input pins 0-3 of the Arduino. If a LED is off, there will be 3.3V on the pin and when a LED is on around 2V. The analog input pins can be used as digital input pins. They return '0' when the input voltage is 0-2.5V and '1' when the input voltage is 2.5-5V. So for the LEDs on the remote: a '0' means LED is on and a '1' means LED is off.

A point of attention here is that the Arduino reads the digital inputs with a speed of less than 1ms. Because the LEDs are blinking with a speed which is much higher than 1ms, a single read action is not enough. With some test code I found out that the LEDs are on for about 70ms and off for 70ms while blinking. I have chosen to read the input pins every 10ms during 100ms to make sure I capture that the LEDs are on.

The Arduino code:

int get_channel(){

  int sel_channel=0;  // the current channel
  int iLED1=1;        // status LED1: 1=OFF, 0=ON
  int iLED2=1;        // status LED2: 1=OFF, 0=ON
  int iLED3=1;        // status LED3: 1=OFF, 0=ON
  int iLED4=1;        // status LED4: 1=OFF, 0=ON
  int iLEDs;          // status LED1-4 in a byte

  // read the LEDs from the remote and determine the current channel
  // make sure we detect a blinking LED, they go on and off for about 70ms
  // the body of the loop takes <1ms, so check every 10ms during 100ms 
  // to capture a full cycle

  for (int i=0; i<10; i++){
    iLED1 &= digitalRead(SOMFY_LED1_APIN);
    iLED2 &= digitalRead(SOMFY_LED2_APIN);
    iLED3 &= digitalRead(SOMFY_LED3_APIN);
    iLED4 &= digitalRead(SOMFY_LED4_APIN);
    delay(10);
  }

  iLEDs = iLED4<<3 | iLED3<<2 | iLED2<<1 | iLED1;
  switch(iLEDs){
    case 14: sel_channel=1; break; // LED1, channel 1 selected
    case 13: sel_channel=2; break; // LED2, channel 2 selected
    case 11: sel_channel=3; break; // LED3, channel 3 selected
    case 7:  sel_channel=4; break; // LED4, channel 4 selected
    case 0:  sel_channel=5; break; // ALL,  channel 5 selected
   
    default:  sel_channel=0;       // no LEDs are blinking
  }

  return sel_channel;
}

Switching the Channels


The select_channel function will switch the remote to a specified channel 'ch'. It works when the LEDs on the remote are not on, but also when the LEDs are already blinking. First, the current channel is fetched. As long as it is not yet equal to channel 'ch', the select button is pressed. The check with 'prev' is needed, because the time between press_button and get_channel could be too short. The remote will not yet have moved to the next channel, so we read the same channel again and press the button again and move 1 channel too far.

todo:
  • if the get_channel function returns '0' (not sure this might happen): do not press the button, but loop again, eventually we have to detect a channel
  • if for some reason, we will never detect a channel due to a malfunction, the loop must be aborted after a while

int select_channel(int ch){

  int curr; // current channel
  int prev; // previous channel

  // while the current channel is not equal to channel 'ch' go to the next
  // channel by pressing the channel select button
  // the button is only pressed when the previous channel selection changed

  // make sure we press the button the first time, channel is never -1
  prev=-1; 
  while((curr=get_channel())!=ch){
    if (curr!=prev){
      press_button(SOMFY_BUTTON_MENU);
      prev=curr;
    }
  }
}

1 opmerking:

  1. Hi Paul,

    I just stumbled across this blog entry and it made my day.
    First of all thank you for your work, especially with the channel selection.

    I gonna build a similar solution with an ESP32 ┬ÁC, connected to a MQTT broker.

    I want to reuse your code for the channel selection and button pressing and publish my code on GitHub.
    You are not mentioning any License, therefore I am asking if you are fine with the use of your code.
    I will mention your blog post, of course.

    Regards,
    Thomas

    BeantwoordenVerwijderen