I2C vs. Rx/Tx

Hello Everyone,

I am working on making an AC light dimmer. The project has two parts: 1) Dimmer, 2) IR remote decoder.

The AC dimmer uses a zero-crossing detector, which signals an interrupt every time the voltage passes through 0. My original plan was to also connect a TSOP IR detector, and then decode TV remote control signals that I could later use to dim or brighten a lamp. Unfortunately, with a 60 Hz AC lines, the interrupt gets triggered too frequently for the Arduino to properly decode the IR signals.

Thus, I am planning on using two Arduino Pro Mini’s; one to decode the IR signals, and one to do the dimming. The Arduino that decodes the signals will then pass the dimming value to the Arduino that performs the dimming.

This finally leads me to my questions.

  1. What is the difference between I2C and Rx/Tx?
  2. Is one faster than the other?
  3. Is Rx/Tx “SPI”?
  4. For my application, which approach would you choose/recommend?

I really appreciate any help.

Thanks!

Dustin

http://www.gammon.com.au/forum/?id=10918

However the Arduino should easily be able to keep up with 60 Hz. Perhaps post your code.

Using rx/tx should be fine... and simpler to set up for you.

Also would have thought that 60Hz would be well within the capability - are you running the minis at 16 or 8 Mhz?

The problem could be with IRremote, being blocked by the dimmer code - but posting the code may reveal a solution. Did you try using IRlib instead, it may perform better???

1) What is the difference between I2C and Rx/Tx? 2) Is one faster than the other? 3) Is Rx/Tx "SPI"? 4) For my application, which approach would you choose/recommend?

every time I want to remind myself of the difference, I just use wikipedia...or just google "i2c vs spi vs uart". Based on what you have posted, I would use Rx/Tx. (or a faster platform like Teensy3.1...assuming resources is the issue)

Hello Both,

Thank you for your comments. Nick, your tutorial is very helpful. AnalysIR, I was using Ken Shirriff’s IR library. Why do you believe IRlib will work better?

I was also not expecting the 60 Hz frequency to be enough to interfere with my code. However, after I experienced this problem, I did a little looking around online and found that other people have experienced a similar issue.

To be clear, I am trying to combine two separate pieces of code: 1) Decode and remember IR signals from TV remotes (based on Ken Shirriff’s Library) and 2) use the decoded signal to change the ‘on’ time of a triac that controls my AC lamp.

Individually, these pieces of code work fine. Originally, I tested the IR part of the code by using it to dim a LED connected to a PWM pin, and it worked great. However, when I tried to combine the two pieces of code, I was suddenly unable to decode any IR remote signals. I tried trouble shooting the problem with the Serial monitor, and found that after combining the two pieces of code, the Arduino was no longer properly decoding the IR remote signal. My (crude) solution was to try to use two separate Arduinos. However, if someone can suggest how this could be done with a single Arduino, that would obviously be preferable.

I look forward to hearing any possible solutions to this problem. Thanks in advance,

Dustin

#include <IRremote.h> 

int AC_LOAD = 3;    // Output to Opto Triac pin
const int irReceivePin = 4;
const int fail_Pin = 6;
const int success_Pin = 7;
const int learn_Pin = 8; // A reset pin to allow me to re-program a different IR remote without turning off the Arduino. Currently, this is not really necessary. However, in the final version of this, I plan on having the IR remote codes stored in non-volatile memory
int brightness = 255;

int i =0;
int val;
const int numberOfKeys = 2;     //  5 keys are learned (0 through 4)
long irKeyCodes[numberOfKeys];  // holds the codes for each key


volatile int dimming = 3000;  // Delay time (in us) from zero crossing
unsigned long last = millis();
IRrecv irrecv(irReceivePin);            //create an IRrecv object
decode_results results;


void setup()
{
 
 // Dimmer code
  Serial.begin(9600);
  pinMode(AC_LOAD, OUTPUT);
  attachInterrupt(0, zero_crosss_int, RISING);
  irrecv.enableIRIn(); 
// Dimmer code ends


// IR code
  pinMode(irReceivePin, INPUT);
  pinMode(learn_Pin, INPUT); 
  pinMode(fail_Pin, OUTPUT); // Flash if the IR signal was failed to be decoded
  pinMode(success_Pin,OUTPUT); // Flash if the IR signal was successfully decoded
  digitalWrite(learn_Pin,HIGH);
  irrecv.enableIRIn();              // Start the IR receiver
  learnKeycodes();                  // learn remote control key  codes
  Serial.println("Press a remote key");
// IR code ends

}


void zero_crosss_int()  //function to be fired at the zero crossing to dim the light
{
  delayMicroseconds(dimming);
  digitalWrite(AC_LOAD, HIGH);
  delayMicroseconds(8.33);
  digitalWrite(AC_LOAD, LOW);
}


void loop()  
{
  long key;
  
  if (digitalRead(learn_Pin) == LOW)
{
  Serial.println("LOW");
  learnKeycodes();
}
  
  
    if (irrecv.decode(&results) == true) 
  {
    
    key = convertCodeToKey(results.value);
    
    if (millis() - last > 250) 
    {      
          
    if(key >= 0)
    {
      Serial.print("Got key ");
      Serial.println(key);
      if (key == 0)
      {
       dimming = dimming+1000;
       dimming = constrain(dimming,10,8000);
       
      }
      if (key == 1)
      {
       dimming = dimming - 1000;
       dimming = constrain(dimming,10,8000);
      }

    }
    
    
    last = millis();
    irrecv.resume();                         // watch out for another message
  }
  
Serial.println(dimming);
Serial.println(key);

}

}



/* All of the IR code begins here:
 * get remote control codes
 */
void learnKeycodes()
{
  digitalWrite(learn_Pin,HIGH);
  while(irrecv.decode(&results))   // empty the buffer
  irrecv.resume();
 
  Serial.println("Ready to learn remote codes");
  long prevValue = -1;
  int i=0;
  while( i < numberOfKeys)
  {
    Serial.print("press remote key ");
    Serial.print(i);
    
    while(true)
    {
      if( irrecv.decode(&results) )
      {
          if(results.value != -1 && results.value != prevValue)
          {
            showReceivedData();  
            irKeyCodes[i] = results.value;
            i = i + 1;
                        
            if (results.decode_type == UNKNOWN)
            {
             i = 0;
            }
            prevValue = results.value;
            irrecv.resume(); // Receive the next value
            break;
          }
        irrecv.resume(); // Receive the next value
      }
    }
  }
  digitalWrite(learn_Pin,HIGH);
  Serial.println("Learning complete");
}

/*
 * converts a remote protocol code to a logical key code 
 * (or -1 if no digit received)
 */
int convertCodeToKey(long code)
{
  for( int i=0; i < numberOfKeys; i++)
  {
    if( code == irKeyCodes[i])
    {
      return i; // found the key so return it
    }
  }
  return -1;
}

/*
 * display the protocol type and value
 */
void showReceivedData()
{
  if (results.decode_type == UNKNOWN)
  {
    flash(fail_Pin);
    Serial.println(" FAILED ");
  }
  else
  {
    if (results.decode_type == NEC) 
    {
      Serial.print("- decoded NEC: ");
      flash(success_Pin);
    }
    else if (results.decode_type == SONY) 
    {
      Serial.print("- decoded SONY: ");
      flash(success_Pin);
    }
    else if (results.decode_type == RC5) 
    {
      Serial.print("- decoded RC5: ");
      flash(success_Pin);
    }
    else if (results.decode_type == RC6) 
    {
      Serial.print("- decoded RC6: ");
      flash(success_Pin);
    }
    Serial.print("hex value = ");
    Serial.println( results.value, HEX);
  }
}


void flash(int pin)
{
for (int i =0;i < 3;i++)
{
digitalWrite(pin,HIGH);
delay(200);
digitalWrite(pin,LOW);
delay(200);
}
}
void zero_crosss_int()  //function to be fired at the zero crossing to dim the light
{
  delayMicroseconds(dimming);
  digitalWrite(AC_LOAD, HIGH);
  delayMicroseconds(8.33);
  digitalWrite(AC_LOAD, LOW);
}

Don't do delays in an ISR? Especially not thousands of microseconds. Find a different way, eg. set up a timer that the first ISR (zero_crosss_int) starts, and when that fires, do what you need to do.

I was using Ken Shirriff's IR library. Why do you believe IRlib will work better?

  1. It is different
  2. It is a more modern re-write of IRremote
  3. It is more configurable, with many more features
  4. It is better documented
  5. It is simply better, in my opinion.

Also, delayMicroseconds takes an ''unsigned int as the parameter (use 8 instead of 8.33)

Waiting in the ISR for up to 8000 uSecs, as Nick pointed out, will stop IRremote's timers from measuring the duration of the IR signal's mark/spaces. (it needs to run every 50uSecs when receiving IR signals).

So the issue is most likely with your ISR & not the 60Hz.

Hi Both,

Thank you for your help. You were correct that the problem with my code was that the delay in my zero_cross was causing my problems. After looking around online, I found a suggestion for a different way to perform the dimming (http://www.instructables.com/id/Arduino-controlled-light-dimmer-The-circuit/). When I use this new code, I can combine my IR code and everything works perfectly. While that is great, the only remaining problem is that I really don’t understand how this new code works.

The new approach makes use of the Timer1 library, and I find it far less understandable than the original code that I posted. Even though this new code is annotated, I really can’t make sense out of it.

As far as I can tell, this is what the code does:

  1. The Timer1 interrupt runs every 65 us, triggering the dim_check function to run
  2. attachinterrupt runs every 8333 us (since it runs every time the voltage rises through zero, and I have 60 Hz lines), and calls the zero_cross_detect function
  3. The zero_cross_detect function sets zero_cross = true, i = 0, and turns the triac off
  4. The dim_check function checks if a zero crossing event occurred, and if it does, it checks if i > dim, and if it is, it turns the triac on, sets i = 0, then sets zero_cross = false. If i < dim, i increases by 1.
  5. The main loop cycles the value of i between 0 and 128 in increments of 1, with an 18 us delay between increments.

While this code works perfectly, I really don’t understand it. Here are a few specific questions

  1. In dim_check, I keep checking if i >= dim. If it isn’t, then i becomes i=i+1. However, dim is constantly changing as well; every 18 us it becomes bigger or smaller by 1. I don’t see what the point of this is, or how it works. Any explanation of what is happening here would be really appreciated.
  2. Why is there an 18 us delay in the main loop?
  3. Overall, how can I use this code to control the dimming level of my light? In the original code that i posted, it was very obvious how to change the dimming level (simply by changing the delay time in the attachinterrupt routine). However, if for example I wanted to always have a medium level of dimming, how would I accomplish this using the new code?

As always, I really appreciate any help in deciphering this code.

Thanks!

Dustin

#include  <TimerOne.h>          // Avaiable from <a href="http://www.arduino.cc/playground/Code/Timer1" rel="nofollow"> <a href="http://www.arduino.cc/playground/Code/Timer1" rel="nofollow"> http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1...</a>

volatile int i=0;               // Variable to use as a counter
volatile boolean zero_cross=0;  // Boolean to store a "switch" to tell us if we have crossed zero
int AC_pin = 3;                // Output to Opto Triac
int dim = 0;                    // Dimming level (0-128)  0 = on, 128 = 0ff
int inc=1;                      // counting up or down, 1=up, -1=down

int freqStep = 65;    // This is the delay-per-brightness step in microseconds.
                      // For 60 Hz it should be 65
// It is calculated based on the frequency of your voltage supply (50Hz or 60Hz)
// and the number of brightness steps you want. 
// 
// Realize that there are 2 zerocrossing per cycle. This means
// zero crossing happens at 120Hz for a 60Hz supply or 100Hz for a 50Hz supply. 

// To calculate freqStep divide the length of one full half-wave of the power
// cycle (in microseconds) by the number of brightness steps. 
//
// (120 Hz=8333uS) / 128 brightness steps = 65 uS / brightness step
// (100Hz=10000uS) / 128 steps = 75uS/step

void setup() 
{                                                   // Begin setup
  pinMode(AC_pin, OUTPUT);                          // Set the Triac pin as output
  attachInterrupt(0, zero_cross_detect, RISING);    // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
  Timer1.initialize(freqStep);                      // Initialize TimerOne library for the freq we need
  Timer1.attachInterrupt(dim_check, freqStep);      
  // Use the TimerOne Library to attach an interrupt
  // to the function we use to check to see if it is 
  // the right time to fire the triac.  This function 
  // will now run every freqStep in microseconds.                                            
}

void zero_cross_detect() {    
  zero_cross = true;               // set the boolean to true to tell our dimming function that a zero cross has occured
  i=0;
  digitalWrite(AC_pin, LOW);       // turn off TRIAC (and AC)
}                                 

// Turn on the TRIAC at the appropriate time
void dim_check() {                   
  if(zero_cross == true) {              
    if(i>=dim) {                     
      digitalWrite(AC_pin, HIGH); // turn on light       
      i=0;  // reset time step counter                         
      zero_cross = false; //reset zero cross detection
    } 
    else {
      i++; // increment time step counter                     
    }                                
  }                                  
}                                   

void loop() {                        
  dim+=inc;
  if((dim>=128) || (dim<=0))
    inc*=-1;
  delay(18);
}