• Wire library for Arduino for working with the I2C bus. LCD I2C module connection to Arduino

    I2C is a serial asymmetric bus for communication between integrated circuits inside electronic devices. That is this protocol communication was designed for internal communication inside a device box or inside a switchboard. He was not tasked with transmitting data to long distances, so there are many myths about the maximum communication range - for some it works poorly even at 50cm, for others at 2m.

    The I2C bus can accommodate up to 128 devices. Addresses from 0 to 127.

    Arduino controllers have I2C physical communication, which allows you to connect to them via two information wires: various sensors, discrete input-output expanders, digital-analog and analog-to-digital converters, as well as other controllers.

    There is no information about transmission speed on the manufacturer's website. But according to the general documentation for the protocol, it should be at least 100 kbit/s


    Now I would like to test how good the I2C bus really is, and how difficult it is to exchange data between several Arduino controllers using it

    I'll take three controllers, connect them with an I2C bus, and figure out how to exchange data over it. The first controller will act as the master, and the remaining two will act as the slave.

    To display the data, I will use a 1602 LCD indicator with an I2C module, which will be connected to the same communication bus.

    The master controller will sequentially poll the second and third controllers. The first controller must display the received data on the indicator. The Arduino Nano slaves will be polled at a frequency of 1 time/sec.

    Connection diagram

    Four wires from each of the 4 devices must be connected in parallel. Conclusion A4 Arduino boards Nano is the SDA bus of the I2C protocol, and A5 is SCL.

    I will use mounting shields for Nano controllers for easy connections.

    Power will simply be supplied to one of the controllers through mini USB entrance.

    The LCD has a default address in the I2C network of 27. For the second controller we will set the address to 2 and for the third to 3. For the leading first controller, the address is not needed.

    The controller program is a master.

    #include #include // Set the LCD address to 0x27 for a 16 chars and 2 line display LiquidCrystal_I2C lcd(0x27, 16, 2); int nano1=0; int nano2; int nano3; void setup() ( Serial.begin(9600); // initialize the LCD lcd.begin(); // Turn on the blacklight and print a message. lcd.backlight(); ) void loop() ( lcd.setCursor( 0, 0); lcd.print(nano1); Wire.requestFrom(2, 2); // request 6 bytes from slave device #8 int i=0;nano2=0; while (Wire.available()) ( / / slave may send less than requested byte c = Wire.read(); // receive a byte as character Serial.print(c); if (i==0) nano2 = ((c & 0xff)<< 8); else nano2 = nano2 | c; i++; } Serial.println(""); Serial.println(nano2); lcd.setCursor(9, 0); lcd.print(nano2); delay(100); Wire.requestFrom(3, 2); // request 6 bytes from slave device #8 i=0;nano3=0; while (Wire.available()) { // slave may send less than requested byte c = Wire.read(); // receive a byte as character Serial.print(c); if (i==0) nano3 = ((c & 0xff) << 8); else nano3 = nano3 | c; i++; } lcd.setCursor(0, 1); lcd.print(nano3); delay(100); nano1++; delay(800); }

    The first controller changes its integer variable and displays its value on the indicator. It also polls the slave with the 2nd and 3rd addresses in turn. Requests two bytes of information from them, converts them into an integer variable. As a result, the first controller rotates three variables from three Nanos and can display them on the indicator.

    Second controller program

    #include int nano2=0; byte high; void setup() ( Wire.begin(2); // join i2c bus with address #8 Wire.onRequest(requestEvent); // register event ) void loop() ( delay(1000); nano2--; ) // function that executes whenever data is requested by master // this function is registered as an event, see setup() void requestEvent() ( high = (nano2 >>

    Third Arduino Nano Program

    #include int nano2=0; byte high; void setup() ( Wire.begin(3); // join i2c bus with address #8 Wire.onRequest(requestEvent); // register event ) void loop() ( delay(1500); nano2--; ) // function that executes whenever data is requested by master // this function is registered as an event, see setup() void requestEvent() ( high = (nano2 >> 8); high = (nano2 & 0xff); Wire.write(high ); // respond with message of 2 bytes Wire.write(high);

    The last two programs differ simply in the address in the Wire.begin(3) function; and the frequency of change of the variable.

    These programs constantly change the integer variable and wait for a request from the master. When requested, this variable is decomposed into two bytes and sent as a response to the request to the master controller.

    Thus, the operation of I2C communication can be monitored by the changing values ​​of three variables on the liquid crystal display.

    Conclusions

    Everything works great - the numbers on the display change. I tried to lengthen the cable between the second and third Arduino controllers. I checked the operation of the communication bus with a length of 3 m - no complaints. I haven’t tried a longer one, but many told me that I2C does not work beyond 0.5 ... 2 m and I was inspired by the 3 m length.

    For myself, I can already see where I will use such a connection between the three Nanos.

    I have not yet tried transferring data from the master to the slave. If you try, please unsubscribe.

    The disadvantages here are short distances noticeably less than superior.

    Arduino supports many data transfer interfaces, one of which is I2C, which is quite popular today. Once upon a time, this communication protocol was invented by Philips and registered under the patented name “I2C”, you can also find it under the names TWI, 2 line interface, but they all work on the same principle.

    The whole point of the I2C bus is that you can hang a large (128) number of various devices, from temperature sensors to microcontrollers.

    But at the same time, I2C is inferior in speed to UART and SPI, due to the basic principles of operation, because two lines are always pulled up to resistors (Vcc), which means that on the graph we get no square pulses, but trapezoidal, unlike the above.

    SDA - responsible for transmitting information (start of transmission, address, data)
    SCL - bus clocking

    In I2C devices can be of two types: Master and Slave

    Now let's look at the basic principles of programming using the standard Wire.h library:

    Wire.begin(uint8_t address) - used to initialize the device, in slave mode you need to enter the address, in master mode Wire.begin() . Instead of Wire, you can use any other word.

    Wire.requestFrom(uint8_t address, uint8_t quantity) – a request to receive a certain number of bytes from a specific device (7 bit address). Returns the number of bytes read.

    Wire.beginTransmission(uint8_t address) - start of transmission

    Wire.endTransmission() - end of transmission, returns error number or success (0)

    Wire.write(uint8_t data) – can take the value of a single byte (value), several bytes (string), an array of a certain length (data, lenght). Located between: beginTransmission and endTransmission. Returns the number of bytes written.

    Wire.available() – returns the number of bytes available for processing. Called by the master after requestFrom.

    Wire.read() – reads a byte from the slave device. It is written after requestFrom.

    Connecting libraries to the Arduino IDE is not difficult, as it comes bundled with a standard editor.

    There are a few more functions, but I think this basics is quite enough for a start, and besides, you can find a library for almost any peripheral.

    For example, consider the connection and operation of the Gy-521 accelerometer and gyroscope.

    We connect according to the diagram (pull-up resistors are built into the module):

    The module can operate on both 3.3 volts and 5.

    #include // connect the library for working with the i2c interface const int MPU_addr = 0x68; // I2C address GY-521 int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ; // variables for writing values ​​void setup() ( Wire.begin(); // initialize the i2c bus Wire.beginTransmission(MPU_addr); // start transmission Wire.write(0x6B); // write certain registers to initialize the Wire module. write(0); // send zero to wake the module from sleep Wire.endTransmission(true); Serial.begin(9600); void loop() ( Wire.beginTransmission(MPU_addr); Wire.write(0x3B); // start with this register Wire.endTransmission(false); Wire.requestFrom(MPU_addr, 14, true); // read all registers AcX = Wire.read();<< 8 | Wire.read(); // 0x3B AcY = Wire.read() << 8 | Wire.read(); // 0x3D AcZ = Wire.read() << 8 | Wire.read(); // 0x3F Tmp = Wire.read() << 8 | Wire.read(); // 0x41 GyX = Wire.read() << 8 | Wire.read(); // 0x43 GyY = Wire.read() << 8 | Wire.read(); // 0x45 GyZ = Wire.read() << 8 | Wire.read(); // 0x47 Serial.print("AcX = "); Serial.print(AcX); // выводим данные в Serial Serial.print(" | AcY = "); Serial.print(AcY); Serial.print(" | AcZ = "); Serial.print(AcZ); Serial.print(" | Tmp = "); Serial.print(Tmp / 340.00 + 36.53); // выводим температуры по формуле Serial.print(" | GyX = "); Serial.print(GyX); Serial.print(" | GyY = "); Serial.print(GyY); Serial.print(" | GyZ = "); Serial.println(GyZ); delay(333); }

    The Arduino LCD display allows you to visually display sensor data. We'll tell you how to properly connect an LCD monitor to Arduino via I2C and look at the basic commands for initializing and controlling the LCD 1602. We'll also look at various functions in the C++ programming language for displaying text information on the display, which is often required to be used in Arduino projects.

    Video. Arduino LCD Display I2C 1602

    LCD 1602 I2C connection to Arduino

    I2C is a serial two-wire bus for communicating integrated circuits inside electronic devices, known as I²C or IIC (Inter-Integrated Circuit). I²C was developed by Philips in the early 1980s as a simple 8-bit bus for internal communication between circuits in control electronics (eg computer motherboards, mobile phones, etc.).

    In a simple I²C system, there may be several slave devices and one master device that initiates data transfer and synchronizes the signal. Multiple slave devices can be connected to the SDA (data line) and SCL (clock line) lines. Often the master device is an Arduino controller, and the slave devices are a real-time clock or LCD Display.

    How to connect LCD 1602 to Arduino via I2C

    The 1602 LCD display with I2C module is connected to the Arduino board with only 4 wires - 2 data wires and 2 power wires. The 1602 display connection is carried out as standard for the I2C bus: pin S.D.A. connects to port A4, output SCL– to port A5. The LCD display is powered from the +5V port on the Arduino. See the 1602 LCD monitor connection diagram in the photo below for more details.

    For this lesson we will need the following details:

    • Arduino Uno / Arduino Nano / Arduino Mega board;
    • LCD monitor 1602;
    • 4 male-female wires.

    After connecting the LCD monitor to Arduino via I2C you will need to install the library LiquidCrystal_I2C.h for working with LCD display via I2C interface and library Wire.h(available in the standard Arduino IDE program). You can download the working library LiquidCrystal_I2C.h for LCD 1602 with an I2C module on the Arduino Libraries page on our website via a direct link from Google Drive.

    Sketch for 1602 display with I2C

    #include // library for controlling devices via I2C#include // connect the library for LCD 1602 LiquidCrystal_I2C lcd(0x27,20,2); // assign a name to the lcd for the 20x2 display void setup () // procedure setup ( lcd.init (); // initialize the LCD display lcd.backlight(); // enable display backlight lcd.setCursor(0,0); // place the cursor at 1 character of the first line lcd.print("I LOVE"); // print the message on the first line lcd.setCursor(0,1); // place the cursor at 1 character of the second line lcd.print("ARDUINO"); // print the message on the second line) void loop () // procedure loop ( /* this is a multi-line comment // initially the void loop() procedure is not used in the sketch lcd.noDisplay(); // turn off the LCD display backlight delay(500); // pause lcd.display(); // turn on the LCD display backlight delay(500); // pause */ }

    Explanations for the code:

    1. the LiquidCrystal_I2C.h library contains many commands for controlling an LCD display via the I²C bus and allows you to significantly simplify the sketch;
    2. The sketch contains a multiline comment /* ... */, which allows you to comment out several lines in the program at once.
    3. Before displaying information on the display, you must set the cursor position with the command setCursor(0,1) , where 0 is the character number in the line, 1 is the line number.

    I decided to make a text-based operator panel (HMI) and connect it via a “square” I2C bus to the Arduino. To do this, I developed a 5-button keyboard board based on the PCF8574P chip.

    PCF8574P is a port expander, DIP package, works on the I2C bus. I purchased a batch of two of these microcircuits for $0.94 with free shipping from China, so one piece costs $0.47. The purchase is profitable, since in local stores these same microcircuits cost more than $2 apiece.

    The HMI display will be a standard 1602 screen, also working via a square bus through the FC-113 board.

    They sent PCF8574P, gave me a tracking number and after 2 weeks I already received them in the mail.


    We take it out of the plastic tube, everything seems to be fine.


    However, on the bottom of the case of one of the microcircuits there are traces of mysterious thermomechanical effects.


    The nature of these damages is not clear to me, but it is obvious that they could not have appeared during shipment.

    I thought about this riddle for a long time until it dawned on me.
    It’s just that Luke Skywalker snuck into the seller’s warehouse, suddenly shrinking to microscopic size. There he noticed one of the port expanders, confused it with an Imperial walker and began hacking at it with a lightsaber. Then the picker came in, saw this picture and said: “Stop it, Luke Skywalker! This is not an imperial walker, this is a PCF8574P microcircuit, for which we have already paid for it from Zaporozhye.”

    It’s good that both microcircuits turned out to be working when tested.

    Let's start creating the keyboard itself according to this scheme.


    In Layout 6.0 I drew a single-sided board.


    You can download the file with the board.

    The board was etched with hydrogen peroxide and citric acid.

    There are many recipes for etching boards with peroxide on the Internet.
    I made the following solution: 100 ml of hydrogen peroxide 3%, 50 g of citric acid, 3 teaspoons of salt. Heated a jar of peroxide in a pan of water.

    We immerse the board in the solution with the pattern down, as recommended when etching with peroxide.

    Psshhhh! At first the process is rapid.


    Ps... Then it noticeably subsides. Turn it over and look at the drawing.


    Beauty.


    The finished board looks like this.




    The address pins of the microcircuit are connected to GND, so the board address on the bus will be 0x20.

    We are writing a program for Arduino.

    #include
    #include

    #define led 13
    #define ADDR_KBRD 0x20
    #define ADDR_LCD 0x27

    String str;
    byte dio_in;
    bool b;
    bool key;

    LiquidCrystal_I2C lcd(ADDR_LCD,16,2); // Set the display

    Void setup()
    {
    pinMode(13, OUTPUT);
    lcd.init();
    lcd.backlight();// Turn on the display backlight
    Wire.begin();

    Wire.beginTransmission(ADDR_KBRD);
    Wire.write(B11111111);
    Wire.endTransmission();
    Serial.begin(9600);
    }
    void loop()
    {
    Wire.requestFrom(ADDR_KBRD,1);
    while (!Wire.available());
    byte dio_in = Wire.read(); //read the status of the PCF8574P ports
    byte mask=1;
    for(int i=0; i<5;i++)
    {
    key[i]=!(dio_in & mask);
    mask=mask<<1;
    }

    Str=String(dio_in, BIN); //
    Serial.println(str);

    B=!b;
    digitalWrite(led, b);

    //
    lcd.setCursor(0, 0);
    lcd.print(String(key)+" "+
    String(key)+" "+
    String(key)+" "+
    String(key)+" "+
    String(key)+" "
    );
    delay(100);
    }


    We load the program into Arduino and connect it to the created keyboard board and display.

    Turn it on, it works!


    My HMI will work not just with Arduino, but with an Arduino-compatible PLC. If there is inspiration and interest from readers, I will write about it someday.

    Pros of PCF8574P:
    1. Minimal strapping.
    2. Easy to use.

    Cons of PCF8574P:
    I didn’t find it on the microcircuit itself, although I advise you to buy from another seller.

    This concludes the review of the PCF8574P microcircuit.
    But as an already experienced observer, in advance I will answer the questions that will definitely be asked:

    Why in DIP package? SOIC is better.
    All other things being equal, I prefer DIP, it’s easier for me.

    DIP and output elements are installed only by lamers; all specialists use SOIC and SMD. So I solder exclusively SMD and in general I’m great.
    Well done.

    Why not just buy a ready-made module on Aliexpress with a 1602 display and a 5-button keyboard? It also works via I2C.
    Its price starts from $11.
    I spent:
    Display 1602 - $1.3
    FC-113 board - $0.55
    Chip PCF8574P - $0.47
    Buttons and caps - $0.7
    Reagents for board etching - $0.3
    Textolite, resistors and other small things - free of charge, from old stocks.
    Total: $3.32
    But the main thing is that on my board I installed buttons with a square pusher to put beautiful colored caps on them.

    Wow, just one PCF8574P chip costs almost as much as an entire FC-113 board!
    Yeah...

    You did everything wrong. They drew it incorrectly, etched it incorrectly in the wrong solution, and placed the wrong buttons. If I were you, I would do everything right.
    Well done.

    Why is the fifth button so far from the rest?
    This is intentional, they are functionally different. Those four are left, right, cancel, enter, and the fifth will be SETUP.

    I was expecting a more exciting story about Luke Skywalker, you deceived me!
    I'm great.

    The I2C data exchange protocol was once developed by Philips. The name I2C comes from the English Iner-IC control or otherwise inter-chip control, Inter-IC, IIC (I2C) is the name of the same protocol.

    This protocol or interface provides high-quality reception and transmission of information (data) from several different devices, for example, you can measure temperature and simultaneously control a digital potentiometer. Communication occurs in software; the algorithm for communicating with the sensor via the I2C protocol is written into the Arduino program (sketch).

    There are special adapters that allow you to connect other devices, for example, with an adapter you can connect an arduino display 1602 (16x2) LCD using the i2c protocol using two wires. On request LCD i2c there is a lot of information on the Internet, here is an example of what an adapter for a display should look like http://www.ebay.com/itm/310565362720

    When working on this interface, one device is the master and the other is the slave. The master initiates transmission and generates the signals necessary for synchronization.

    The slave, in turn, depends on the master, and begins transmitting data only after receiving a command from the master device.

    A device connected to the I2C bus has its own unique address. It is to this address that the master device is addressed.

    An example of connecting sensors using the I2C protocol

    The connection is made via two wires: SCL - the clock signal or clock signal and SDA - the data signal. At the same time, any number of different sensors (slave devices) with their own unique IDs can be connected to the I2C bus.

    The acquaintance begins with a library specially written for these purposes, whose name is Wire. Before starting work, it must be imported into the project; it has special commands or methods for “communicating” with devices.

    To exchange data with devices you need to know their id. Different devices may have different address lengths (id) of 8 or 7 bits. The Wire library uses 7-bit addressing.

    The slave devices are connected to the pins on the Arduino board. Each version of Arduino has its own I2C pins

    • UNO - A4(SDA), A5(SCL);
    • Mega - 20(SDA), 21(SCL);
    • Leonardo- 2(SDA), 3(SCL);
    • Due - 20(SDA), 21(SCL),SDA1,SCL1;

    Example program code for controlling a digital potentiometer using the Wire library

    This example shows how to set a specific resistance value in a digital potentiometer using the Wire library. Setting a specific value is done using the val variable.

    #include void setup() ( Wire.begin(); // join i2c bus (address optional for master) ) byte val = 0; void loop() ( Wire.beginTransmission(44); // transmit to device #44 (0x2c) // device address is specified in datasheet Wire.write(byte(0x00)); // sends instruction byte Wire.write(val ); // sends potentiometer value byte Wire.endTransmission(); // stop transmitting val++; // increment value if (val == 64) // if reached 64th position (max) ( val = 0; // start over from lowest value ) delay(500);

    Video of I2C and Arduino working. Part 1