IR Fernbedienung mit Relais Modul und Portexpander MCP23017

Have you ever been in Miniature Wunderland in Hamburg? The world on 1:87 scale can be seen there. It is worth it for everyone between eight and eighty. After my last visit I rebuilt my own model railway. Now I want to try to automate a lot on my small system with my knowledge of microcontrollers. I would like to switch the soft and light circuits via infrared remote control. In my scrap box, I still have several remote controls from decommissioned CD/DVD players etc. Should hit.

Preliminary considerations

The switches are permanently connected to light flow (12 - 18Volt). In order to switch the built -in electromagnets, the connection to mass is briefly established with a button. Due to the above voltage, it is advisable to use relays, two of each soft or switch pair if two switches are switched at the same time. A certain number comes together quickly. Fortunately, there are the relay modules with 8 or 16 relays.

Hardly any microcontroller has so many GPIO pins, so clear case for the Port Expander MCP23017, which I had already presented in a three-part blog two years ago (Part 1, Part 2, part 3). With a port expander you get sixteen additional GPIOs, enough for eight soft. For reasons of clarity, I am planning a second port expander for the lighting circuits, in which not only a short mass impulse is switched, but the light should be switched on or off. Up to eight port expander can be switched in parallel, since the I2C address in the 0x20 to 0x27 area can be selected.

I need an IR receiver and a suitable program library for the infrared remote control. I favor the little one Infrared sensor module, available individually, or part of the 35 in 1 sensor kits is. These small modules have an LED that flashes with the signal reception. This excludes a source of error in the experiments and in operation if something does not work. Basically, the individual IR sensor, e.g. TSOP4838 or VS1838, is also sufficient, but it makes no difference in terms of price.

Hardware

1

Microcontroller ATMEGA 328

1

Infrared sensor module

Opt.

35 in 1 sensor kit

1

16-relay module 12V with optocoupler

and or

8-relay module 5V with optocoupler

and or

4-relay module 5V with optocoupler

1 - 2

Port Expander MCP23017 CJMCU-2317 alternative click

1 - 2

10 kΩ resistance

1

(Mini-)Breadboard, jumper cable


Circuit and sketch for my IR receiver

First of all, I install Armin Joachimsmeyer's library in Arduino IDE. The connection of the three pins should not be too difficult.

I first look at the list of sample programs simpler each to make the first attempts with my Philips-IR remote control.

At the beginning there are many associated lines for the remote controls of different manufacturers. The comment says that all protocols are active if no special has been defined.

 /*
  * Specify which protocol (s) should be used for decoding.
  * If no protocol is defined, all protocols are active.
  */
 //#define decode_denon // included Sharp
 //#define decode_jvc
 //#define decode_kaseikyo
 //#define decode_panasonic // The same as decode_kaseikyo
 //#define decode_lg
 #define decode_nec// Includes Apple and Onkyo
 //#define decode_samsung
 //#define decode_sony
 //#define decode_rc5
 //#define decode_rc6
 
 //#define decode_bosewave
 //#define decode_lego_pf
 //#define decode_magiquest
 //#define decode_whynter
 
 //#define decode_distance // Universal decoder for pulse width or pulse distance protocols
 //#define decode_hash // Special decoder for all protocols
 
 //#define debug // Activate this for lots of lovely debug output from the decoders.
 //#define info // to see value information from universal decoder for pulse width or pulse distance protocols

The last line sounds promising, because I didn't find the name Philips in the lines before. I will comment on this line in order to obtain further information about my remote control. But where do I have to connect the data pin? At first glance, I don't see a corresponding line in the sketch. But on the second.

The line is in the sketch:

 #include "PindefinitionSandmore.H"

The file is in the same subdirectory as the sketch Cind definition sandmore.hthat is opened together with the sketch and included with the above line. Here I find PIN 2 for my AVR microcontroller.


So, minus (-) to GND, the middle pin on 3.3V or 5V and the signal pin s on pin 2 of the microcontroller.

When pressing the key 1, I will receive the following edition in the serial monitor:

I only get two lines because I pressed the "neat" button, i.e. a little longer, so that two IR signals have actually been sent. If I only stipulate a button very briefly, I only get one line.

The information behind Protocol = is important for my project, with the corresponding line with RC6 I will remove the comment lines //, as well as the value behind Command =, which contains the code for the pressed button.

At this point I write down the respective values ​​behind Command = for the buttons that I would like to use later.

It was all easier when I imagined it. Thanks to Armin Joachimsmeyer for his program library.

Now I turn to the Port Expander MCP23017. I read the basics again in Part 1 of my blog series on July 20, 2020. Here is a short repetition:

The 28 connections are quickly explained, because 16 of them (GPB0 to GPB7 and GPA0 to GPA7) are our GPIO pins gained. Voltage supply VDD (Pin9) goes to 3.3V or 5V, VSS (Pin10) to GND, connections 11 to 14 are the data lines. I2C only needs SDA (13) and SCL (12), 11 and 14 are not connected (NC = Not Connected, these are required for the port expander with SPI, the MCP23S17).

As mentioned above, the I2C address can be selected via the A0 to A2 connections. If all three are concerned (low), the I2C address is 0x20. This is the basic address, by connecting individual pins to high, the resulting number is added. With the second port expander we will put A0 on high to receive address 0x21.

As usual, the cross line above the reset at connection 18 means the negation of the entrance. If the IC is to be reset, this connection must be briefly connected to GND/LOW. Conversely, this means that there must be high -concern here in normal operation. In order to avoid a short circuit during the reset, we connect PIN 18 via a pullup resistor of 10 KOHM with VDD

The connections inta (20) and IntB (19) are intended for so -called interrupts. I will not use this.

Programming and data exchange takes place in the MCP23017 via registers with 8 bits. An extensive chapter that we can significantly shorten by using a program library. At this point, the 42-page data sheet is only quoted, which is actually used in this project.

To get you in the mood, I install the MCP23017 program library from Bertrand Lemasle and look at the files MCP23017.H and MCP23017.cpp and the example program Portcopy, in which the corresponding notice is switched on at an input of port B on port A .


 /**
  * On every loop, the state of the port b is copied to port a.
  *
  * Use active low inputs on port A. Internal pullups are enabled by default by the library so there is no need for external resistors.
  * Place LEDS on port B for instance.
  * When pressing a button, the corresponding led is shut down.
  *
  * You can also uncomment one line to invert the input (when pressing a button, the corresponding led is lit)
  */
 #include
 #include
 
 #define MCP23017_ADDR 0x20
 MCP23017 mcp = MCP23017(MCP23017_ADDR);
 
 void setup() {
     Wire.begin();
     Serial.begin(115200);
     
     mcp.init();
     mcp.portMode(MCP23017Port::A, 0);          //Port A as output
     mcp.portMode(MCP23017Port::B, 0b11111111);   //Port B as input
 
     mcp.writeRegister(MCP23017Register::GPIO_A, 0x00);  //Reset port A
     mcp.writeRegister(MCP23017Register::GPIO_B, 0x00);  //Reset port B
 
     // GPIO_B reflects the same logic as the input pins state
     mcp.writeRegister(MCP23017Register::IPOL_B, 0x00);
     // Uncomment this line to invert inputs (press a button to lit a led)
     //mcp.writeRegister(MCP23017Register::IPOL_B, 0xFF);
 }
 
 void loop() {
     uint8_t currentB;
 
     currentB = mcp.readPort(MCP23017Port::B);
     mcp.writePort(MCP23017Port::A, currentB);
 }

I can take the first four lines of the program completely and integrate them into the IR program. The library Wire.h is needed for I2C, with the library MCP23017.h the object mcp is instantiated at the address 0x20.

 #include 
 #include
 
 #define MCP23017_ADDR 0x20
 MCP23017 mcp = MCP23017(MCP23017_ADDR);

For my project I need all GPIO ports as outputs. For this only the methods mcp.init(), mcp.portMode(), mcp.writeRegister() and mcp.writePort() are used.

First, mcp.init() is used to write the special register IOCON (for I/O control) with specified values. We have to insert this instruction in the setup() function after initializing I2C.

With mcp.portMode(MCP23017Port::A, 0b000000); we define that the eight ports (here GPA0 to GPA7) act as output. For port B there are two choices: On the one hand, you can use mcp.portMode(MCP23017Port::B, 0b111111); to define port B with eight inputs, as in the example sketch, and then connect the conventional control panel of the railroad system in parallel. Or you can define port B as an output and connect another eight relays.

With mcp.writePort(MCP23017Port::A, 0bxxxxxx); we write the port with the desired values to switch one relay at a time. More about this in a moment.

So as an intermediate result, I merge the two example sketches SimpleReceiver and PortCopy. Now I only have to query in the function loop(), which key of the IR remote control was pressed and assign a code in if - or else if - branches, with which the correct relay is switched in each case with the help of the port expander. For this we look again at the command mcp.writePort(MCP23017Port::A, 0bxxxxxx);. Each x stands for a 0 or 1 at the outputs GPA7, GPA6, ..., GPA1, GPA0. As input we can set the respective power of two of the output, i.e. 128, 64, ..., 2, 1. So these powers of two are the codes for our eight relays.

Because of the negative logic, I apply the value 255-code to the desired relay, and only for 500ms. After that, all relays are reset again.

     mcp.writePort(MCP23017Port::A,255-code);
     delay(500);
     mcp.writePort(MCP23017Port::A, 255);

Here is all the code for my test setup with eight relays. Actually, I did the tests with only two turnouts and four relays. See picture below.

Sketch Download

 #define DECODE_RC6 // works fine with my Philips DVD Remote Controller
 #include
 /*
  * Define macros for input and output pin etc.
  */
 #include "PinDefinitionsAndMore.h"
 #include
 
 // new lines for MCP23017 Port Expander
 #include
 #include
 
 #define MCP23017_ADDR 0x20
 MCP23017 mcp = MCP23017(MCP23017_ADDR);
 
 int code = 0;
 
 void setup() {
     Wire.begin();
     Serial.begin(115200);
     // Just to know which program is running on my Arduino
     Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE));
 
     // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED
     IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
 
     Serial.print(F("Ready to receive IR signals of protocols: "));
     printActiveIRProtocols(&Serial);
     Serial.print(F("at pin "));
     Serial.println(IR_RECEIVE_PIN);
 // new lines for MCP23017 Port Expander
     mcp.init();
     mcp.portMode(MCP23017Port::A, 0b00000000);          //Port A as output
 //   mcp.portMode(MCP23017Port::B, 0b11111111); //Port B as input
     mcp.writeRegister(MCP23017Register::GPIO_A, 0b11111111);  //Set port A to HIGH for relays
 //   mcp.writeRegister(MCP23017Register::GPIO_B, 0b00000000); //Reset port B
 
     // GPIO_B reflects the same logic as the input pins state
     //mcp.writeRegister(MCP23017Register::IPOL_B, 0x00);
     // Uncomment this line to invert inputs (press a button to lit a led)
 //   mcp.writeRegister(MCP23017Register::IPOL_B, 0xFF);
 }   // end of void setup()
 
 
 void outputCode(int code)   {
     Serial.print("Code = ");
     Serial.println(code);
     mcp.writePort(MCP23017Port::A,255-code);
     delay(500);
     mcp.writePort(MCP23017Port::A, 255);
 }    // end of function
 
 void loop() {
     if (IrReceiver.decode()) {
         // Print a short summary of received data
         IrReceiver.printIRResultShort(&Serial);
         if (IrReceiver.decodedIRData.protocol == UNKNOWN) {
             // We have an unknown protocol here, print more info
             IrReceiver.printIRResultRawFormatted(&Serial, true);
        }
         Serial.println();
         /*
          * !!!Important!!! Enable receiving of the next value,
          * since receiving has stopped after the end of the current received data packet.
          */
         IrReceiver.resume(); // Enable receiving of the next value
         /*
          * Finally, check the received data and perform actions according to the received command
          */
         int received = IrReceiver.decodedIRData.command;
         Serial.print("received = ");
         Serial.println(received);
         
         if (IrReceiver.decodedIRData.command == 1) {
           code = 1;
           outputCode(code);
        }
         else if (IrReceiver.decodedIRData.command == 3) {
           code = 2;
           outputCode(code);
        }
         else if (IrReceiver.decodedIRData.command == 4)   {
           code = 4;
           outputCode(code);
            }
         else if (IrReceiver.decodedIRData.command == 6)   {
           code = 8;
           outputCode(code);
          }
         else if (IrReceiver.decodedIRData.command == 7) {
           code = 16;
           outputCode(code);
        }
         else if (IrReceiver.decodedIRData.command == 9) {
           code = 32;
           outputCode(code);
        }
         else if (IrReceiver.decodedIRData.command == 131)   {
           code = 64;
           outputCode(code);
            }
         else if (IrReceiver.decodedIRData.command == 239)   {
           code = 128;
           outputCode(code);
          }          
         else   {
           code = 0;
           Serial.print("Code = ");
           Serial.println(code);
          }
 //       delay(500);       //avoid double input
    }
 }   // end of void loop()

Please remember the file PinDefinitionsAndMore.h in the same directory.

By multiple request here is the schematic:

circuit diagram

A Darlington transistor is not necessary, but an external power supply for the relays is if there are more than two. Please also have a look at the older posts linked above.

P.S. My wife objected to me drilling the living room table to make the cables disappear. On the plant are MCU, relays and cables underneath the plate.

Für arduinoProjekte für anfänger

7 comments

Bernd Albrecht

Bernd Albrecht

@ H.-J. Spitzner zum Thema Relais: Danke für den Hinweis, dass Relais – wie auch Motoren und Servos – am besten aus einer externen Spannungsquelle versorgt werden. Dafür wird m.E. jedoch kein ULN 2803 benötigt, denn die Relais-Module bieten für die externe Spannung einen Anschluss, meist gebrückt mit einem Jumper. Wenn dieser entfernt wird, kann man hier die Spannung, z.B. 6V, direkt anschließen.

Andreas Wolter

Andreas Wolter

wir haben einen Schaltplan ergänzt. Bitte auch einen Blick in die verlinkten Blogbeiträge werfen.

Grüße,
Andreas Wolter i.A. Bernd Albrecht
AZ-Delivery Blog

Hans-Georg Müller

Hans-Georg Müller

Moin,
wie immer Klasse. Aber wie Hans schon schrieb, der Schaltplan wäre schon wichtig bei der Menge von Bauteilen.
Danke

Michael Arnold

Michael Arnold

Vielen Dank für das Super Programm. Habe es Heute ausprobiert und es Funktioniert einwandfrei.

H.-J. Spitzner

H.-J. Spitzner

Ich habe dies in einer Anlage ähnlich gehandhabt, allerdings fällt mir ein Problem auf, dies ist die Variante mit dem “delay(500)”. Wenn zuviele Relais geschaltet werden sollen, ist diese Arbeitsweise etwas hinderlich. Ich habe dazu jedem Ausgang (ob Relais oder etwas anderes ) einen eigenen Timer (z.B. Zähler im Sekundentakt), welcher dann die Rücksetzung macht. Dadurch sind die Kommando-Eingabe sofort wiederfrei. Außerdem ist es hier wichtig, da 6 * 328p und 1 MEGA (196 Tasten und LED) in einem Netzwerk (RS485) sich gegenseitig Datensätze zu senden, da sollten keine delay’s, die Abfragen behindern. Wenn man vorher plant wieviel Relaise reinkommen und der Prozeß konkret ist, sollte es laufen, aber später alles umbauen (software neu schreiben) dauert länger.

P.S. als Nachtrag: Bei der Schaltung zum STeuern von Relais usw. sollte man auch den Strombedarf in Betracht ziehen! Als Ergänzung ist hier der ULN 2803 (8*Darlington-Transistorpaar mit Open_Kollektor) zu nennen.
viel Erfolg!

Hans

Hans

Danke für die ausführliche Zusammenfassung. Eine Frage: Wo kann ich den Schaltplan finden? Oder habe ich es nur überlesen?
Mit freundlichem Gruß Hans

jordi

jordi

Apreciades proveïdores, agradezco la càlida de sus productors y desfaria comentar que encuentro a faltar documentacion en Castellanos y a poder ser en catalana se, el esfuerzo que esto supone però para gente inexperta supone una gran ajuda. Gràcies

Leave a comment

All comments are moderated before being published