tl;dr can someone tell me if it is possible to set custom pins for an I2C slave (client, peripheral) within the Arduino environment?
Context
On an ESP32 (ESP32-WROVER from Freenove) I am trying to communicate with 2 devices that are I2C masters (Adafruit Monster M4sk)
On this particular ESP32-WROVER board the default SDA and SCL pins are used by a camera.
So I have to set up I2c on different pins. I'm using pin 2 as SDA and pin 15 as SDL.
I can easily set up I2C as a MASTER on those pins, works just fine using Wire.begin(2,15). I find lots of documentation about setting custom pins, multiple busses using Wire or TwoWire.
What I really want to do is something like this:
#include <Wire.h>
#define SDA1 2
#define SCL1 15
#define SDA2 21
#define SCL2 22
#define SLAVE_ADDRESS_ON_BUS_1 0x52
#define SLAVE_ADDRESS_ON_BUS_2 0x33
setup()
{
Wire.begin(SDA1,SCL1,SLAVE_ADDRESS_ON_BUS_1); // Join I2C bus 1 using pins 2 and 15
Wire1.begin(SDA2,SCL2,SLAVE_ADDRESS_ON_BUS_2);// Join I2C bus 2 using pins 21 and 22
Wire.onReceive(receiveI2CBus1Event); // register event for when master on i2c bus 1 writes
Wire.onRequest(WriteToI2CBus1Event); // register event for when master1 wants on i2c bus 2 wants to read
Wire1.onReceive(receiveI2CBus2Event); // register event for when master on i2c bus 2 writes
Wire1.onRequest(WriteToI2CBus2Event); // register event for when master on i2c bus 2 wants to read
}
As far as I can tell there is no way to use either Wire or TwoWire to create a peripheral on a custom set of pins...
Wire.begin(MY_ADDRESS);
I have tried re-defining SDA and SCL but that does not seem to work
Note I am running Expressif's ESP32 libraries v2.0.2 (ESP32 Libraries 1.0.6 and prior did not support ESP32 as a slave)
I have tried this (this being redefining SDA and SCL) using both Arduino IDE 1.8.19 and Arduino IDE 2.0.0.rc5
I can't be the first person trying to have an ESP32 act as an i2c slave using something other than the default pins...
Am I gonna have to resort to some sort of I2C bridge/switch/mux? if so, any recommendations? (preferably recommendations with arduino sample code showing how a master can assign an address to another master)
Thank you.
You're very close, you just have the order of the parameters flipped. The address comes first, followed by the pin numbers.
There are optional parameters to Wire.begin() that allow you to specify the pins used for the I2C controller in slave mode:
bool begin(uint8_t slaveAddr, int sda=-1, int scl=-1, uint32_t frequency=0);
So you could call
Wire.begin(MY_ADDRESS, SDA1, SCL1);
and optionally specify the frequency as a final argument if you need to.
While this form of begin() looks like it's for a master, its code explicitly sets the is_slave flag:
is_slave = true;
and calls i2cSlaveInit().
I haven't actually used this so I can't promise it works, but this is the way the code is organized.
Related
I am trying to connect a Waveshare e-Paper display via SPI to the ESP32 board, but I can't get it to work. I am new to the electronics aspect, so I am not sure if I am connecting the pins correctly.
Waveshare provides example code for Arduino but when I flash it to my board the display does not do anything. I can see in the serial output that the sketch is flashing fine and running on the board.
I tried editing the code in the example code where the SPI.begin() happens and pass in the pins I am using (on this line).
After doing that, the code runs past the following block, so it appears the epd.init() works now.
if (epd.Init() != 0) {
Serial.print("e-Paper init failed");
return;
}
The hardware I am using:
E-Paper display: Waveshare 4.2inch E-Ink display module
Driver board: ESP32 Heltec wifi LoRa (V1)
ESP32 datasheet
If I am on a wrong path all together or if I am meant to use a different library (I came across GxEPD2, not sure if I am meant to use that one instead?), please feel free to correct me and point me in the right direction. Any help is much appreciated!
My main questions:
How do I connect the SPI display, which Pins to use?
Which library to use to display stuff on the display?
Example code of how to get started?
I am afraid that it will be hard to connect the ePaper module to the ESP32 Heltec using the standard library from Waveshare. It is probably do-able, but will require some modification on the standard ePaper library. Looking at epd-library-arduino gives these pins for SPI:
// Pin definition
#define RST_PIN 8
#define DC_PIN 9
#define CS_PIN 10
#define BUSY_PIN 7
All these pins are not freely available on the Heltec. In addition the Heltec uses SPI for LoRa and OLED. You would have to find some other pins and possibly do SPI "bit banging" (not hardware SPI). I would suggest starting with the example code from Waveshare on a ESP-WROOM-32 chip without any other external devices connected to the SPI bus. When you get this working, start porting the code over to the Heltec using different pin setup for the SPI.
GxEPD2 is a great library. I've personally used it on a ESP-WROOM-32. Note that GxEPD2 is licensed under GPLv3.0.
Both Waveshare, GoodDisplay, GxEPD2 and Adafruit GFX have a lot of example source code to get started.
I don't know if you got any further, but I am on a similar mission with a 5.65 7 colour display. I got the EPaper working using the Driver code here ESP32 Driver Board Code, but then found it was interfering with the LoRa (which had been working fine)! I am going to dig deeper but at this stage (rightly or wrongly) I made these changes to DEV_Config.h under the library and if you aren't using LoRa then at least you can validate your EPaper is working :
#define EPD_SCK_PIN 5
#define EPD_MOSI_PIN 27
#define EPD_DC_PIN 13
#define EPD_CS_PIN 17
#define EPD_RST_PIN 14
#define EPD_BUSY_PIN 2
/*
#define EPD_SCK_PIN 13
#define EPD_MOSI_PIN 14
#define EPD_CS_PIN 15
#define EPD_RST_PIN 26
#define EPD_DC_PIN 27
#define EPD_BUSY_PIN 25
*/
I wired the pins accordingly. My theory being it should be able to work on the same SPI as the LoRa, with separate CS Pin. I will be messing with this for a while so maybe come up solution. This may be related to why I was having troubles with the SDCard module I was also trying to get working on same circuit. I am a newbie so go easy on me!
I try to communicate, read and write, from Arduino - slave - to RPi - master - with Android Things.
If i R/W, with a level converter, from RPi to Arduino 5v (16Mhz), everything works fine.
So i decide to eliminate the level converter, and use a 3v3 Arduino mini pro (8Mhz).
The write works fine, but when i try to read from the Arduino, the signal stops.
5v_16Mhz
After the Setup to 9, 0 address, and reads to 9, the signal still low and received the data. No problem.
3v3_8Mhz
After the Setup to 9, 0 address, and read to 9, the signal goes high and the data stop.
I used the same example for the Slave:
#include <Wire.h>
byte RFID[20] = {9,8,7,6,5,4,3,2,1,1,2,3,4,5,6,7,8,9,1,2};
void setup() {
Wire.begin(8); // join i2c bus with address #8
Wire.onRequest(requestEvent); // register event
Wire.onReceive(receiveEvent); // register event
Serial.begin(115200); // start serial for output
pinMode(13, OUTPUT);
}
void loop() {
delay(100);
}
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
Serial.println("Master ask");
digitalWrite(13, HIGH);
delay(250);
Wire.write(RFID, 20);
digitalWrite(13, LOW);
}
// function should be executes whenever data is received from master
// this function is registered as an event, but it's called every time the RPi
// call the Device.
void receiveEvent(int howMany) {
while (0 < Wire.available()) {
byte RTC_syn = Wire.read(); // receive byte
Serial.println(RTC_syn);
}
}
I really don't know how drives the signal high...
Someone can help me?
If i R/W, with a level converter, from RPi to Arduino 5v (16Mhz), everything works fine.
So i decide to eliminate the level converter, and use a 3v3 Arduino mini pro (8Mhz).
The write works fine, but when i try to read from the Arduino, the signal stops.
This is because level converter you had in the 5V/3.3V version does more than shift the voltage. It also acts as a nice high-impedance buffer between the two devices that helps keep the signal driven and avoids loading effects.
Without the buffer, your bus is likely experiencing a bit of loading. You can try to combat this by adding stronger pull-up resistors. The RPi3 has 1.8k pull-up resistors on the I2C lines, which generally works but can be marginal depending on the input impedance of the slave device. The Arduino Mini has pads to install I2C pull-ups but there are none by default.
The recommended pull-up resistance for a pure 3.3V I2C bus is closer to 1k, so you likely just need to add some stronger pull-ups between SCL/SDA and +3.3V. Anything you add will be in parallel to the RPi3 resistors so factor that into your calculation. For example, adding 4.7k resistors brings the effective resistance down to about 1.3k.
If you are unable to solve it with pull-ups, you can achieve the same buffer effect without level translation by using a line driver IC (random example).
If the level converter works, you should stick with it.
Communication protocols like I2C encode data into a series of logic HIGH and logic LOW signals. What does HIGH / LOW mean? It depends on the devices. For the majority of embedded devices, logic LOW will be ground, 0V.
For Arduinos and Raspberry Pis, the source voltage is different (3.3V versus 5V). This difference can lead to several potential issues.
The 5V signal is too high for the Arduino to handle, causing the Arduino to stop working or reboot
The 3.3V signal is not strong enough to be interpreted as logic HIGH. Embedded devices have circuits that round signals to HIGH/LOW, and the thresholds may not be entirely even. A 5V input may only accept 4.5V or higher, interpreting everything else as LOW or in an indeterminate state.
I think my question is more of a lack of comprehension, but here is an overview:
I'm trying to discuss with an old Mitsubishi which uses serial communications. The initialization works by toggling the K-Line at 5 bauds (this technique seems to be used elsewhere), then pass the K-Line as INPUT, read some code, and after that you can read and write directly on the K-Line. The details of said protocol (and the fact that we are only using one line) are detailled here.
This messes me up a bit, since I'm used to a TX pin and an RX pin, not having to switch a pin between TX and RX after each read and write, but let's assume this works.
How can I set up a serial communication (specify baudrate and RX/TX pins, which will be the same here) on a Teensy 3.2 ? (classic Arduino C++ stuff)
Or maybe I can have two pins, each connected to the K-Line. This is physically the same but allow me to define it in software as a standard serial.
Here is the written code so far:
void setup() {
// HERE: SETUP SERIAL WITH ONE PIN
// Or do the two-pins-on-same-wire-thing with HWSerial.begin(15625, SERIAL_8N1);
pinMode(DATA_PIN, OUTPUT);
// MUT Init
int init = MUT_INIT_CODE;
for (int i= 0; i < 9; i++){
if (init & 1){
digitalWrite(DATA_PIN, HIGH);
digitalWrite(LED_PIN, HIGH);
} else {
digitalWrite(DATA_PIN, LOW);
digitalWrite(LED_PIN, LOW);
}
init >>= 1;
delay(MUT_INIT_DELAY);
}
byte answer[3];
pinMode(DATA_PIN, INPUT);
HWSerial.readBytes(answer, 3);
}
Thanks in advance!
Technically you can not tie RX and TX together, because a hardware serial module (like the one inside the Teensy) will drive TX high by default, so RX would be always high as well. You need to configure your pin sometimes as an input, sometimes as an output.
You can not use a hardware serial module because the TX and RX pins are predefined and separate, and you should try using a software serial emulation library like https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
Try changing the code to add a function to reconfigure your unique pin as an input or output, as needed, and declare the same pin for TX and RX.
edit: a hardware approach would be to use an analog multiplexer like the MAX4619. This way you can use a hardware serial module on the Teensy, connect TX and RX to the multiplexer's X0/X1 pins, and the K-line on the X pin. You can toggle the multiplexer's A command pin with a Teensy GPIO.
I didn't test this approach because I don't have this kind of car, but it might be worth giving it a try.
edit2: to answer more specifically the op's question, the pins of the serial modules of the teensy are hardwired. On a Teensy 3.2, Serial1 is connected to pins 0/1 or 5/21, Serial2 to pins 9/10 or 26/31, etc. The list is available here: https://www.pjrc.com/teensy/td_uart.html
The baud rate is configured by calling Serial1.begin(YOUR_BAUD_RATE);
Im just learning how to use the Arduino, so i bought a Arduino UNO that comes with the ATMega328. And just to start learning i was thinking to use the controller as a buffer:
How would be the code for that? What pins can i use for a RS232 signal?
Thanks a lot.
Uhm... Sorry but.. What? What do you want to do?
If you just want to use it as a buffer you can do this
const int inputPin = 2;
const int outputPin = 1;
void setup() {
pinMode(inputPin, INPUT);
pinMode(outputPin, OUTPUT);
}
// the loop routine runs over and over again forever:
void loop() {
digitalWrite(outputPin, digitalRead(inputPin));
}
But this is absolutely useless.
If you want to start from the beginning start reading the examples provided with the IDE. Starting from the classic example (blink a led).
As for the RS232.. You can't connect to a RS232 signal (because the RS232 signal is a bipolar signal which can range from -15V to 15V). If you want you can convert the RS232 voltage levels to UART levels (0-5V) with an IC (the most famous one is the MAX232).
And then.. You can either connect it to the TX and RX pins (but doing so you exclude the USB port) or to two other pins and then use the SoftwareSerial library.
But usually people use the integrated USB-Serial converter on the board: when you connect the Arduino to the PC your computer detects a new serial port..
I have an Arduino Mega and would like to change the SDA&SCL pins from A4&A5 to A14&A15.
So that I can control an I2C LCD from there, I have the library but I don't see where the pins are set; however, I would imaging they have to be set somewhere...
I am new to c++ and libraries so my eyes may just be skipping over it
The standard i2c library uses dedicated hardware, which is tied to certain pins. To send a byte out in this way, your program writes a byte to a certain register (this will take just a few clock cycles), and the hardware takes care of shifting the bits out one by one on the SDA pin and toggling the SCL pin automatically.
What you probably looking for is software i2c, which implements the same protocol in software and should allow you to use arbitrary pins. It is likely that this library is considerably slower and uses more resources than the standard one: When your program wants to send a byte, the library has to extract a single bit, lookup which data pin you had defined, write the bit-value to that pin pin, lookup which pin you had defined for the clock, toggle that pin, wait a bit, toggle the clock again and so on, all in software. This will take a lot of time, but maybe you don't care in your application.
I2C pins are set in hardware in AVRs; there is no way to change them other than to use a completely different I2C bus, assuming the MCU even has any others in the first place.
If you want to use pins other than those available in hardware then you'll need to find a library that bit-bangs I2C over normal GPIOs, and then modify the LCD library to use that library instead of hardware I2C.
This question is quite old, but I would like to suggest this LiquidCrystal Software I2C library, because I didn't found many other working resources online and this library implements I2C protocol in software, so you can use any input/output pin of your Arduino.
Just need to specify SDA and SCL pins as 4th and 5th arguments when creating the LiquidCrystal_I2C object and then has the same functions of the standard LiquidCrystal_I2C library.
So, for example, you if you want to use pin 3 and 4, as SDA and SCL respective, your hello word will be:
// https://github.com/francesco-scar/LiquidCrystal_Software_I2C
// Based on https://github.com/johnrickman/LiquidCrystal_I2C project
#include <LiquidCrystal_Software_I2C.h> // Include library
LiquidCrystal_I2C lcd(0x3f, 16, 2, 3, 4); // Set the LCD address to 0x27 for a 16 chars and 2 line display
void setup() {
lcd.init(); // LCD initialization
lcd.backlight(); // Turn on backlight
lcd.print("Hello, world!"); // Print Hello, world!
}
void loop() {
}