Solved I2C EZO component
-
HI Everybody,
I'm new with Rexygen, but I'm really surprise with the amount of possibilities, it's really amazing.
I have already done some I/O simulation, and everything works perfectly.My project with Rexagen would be to integrate Ph / ORP /etc probes into my swimming pole management.
To do that I'll add EZO I2C components ( https://www.whiteboxes.ch/shop/ezo-ph-circuit/) on the GPIO of my raspberry.
I'm not familiar on how I can manage I2C data with rexygen.Is someone can guide on how to proceed ?
Arnaud
-
There are a few I2C examples in Rexygen studio.
-
Hi Arnaud,
you can use the Rexlang block for I2C communication. As Scoobsalamander has mentioned, there are some I2C examples that can be used as a starting point of your own implementation. Use the Start from an Example Project choice in the Rexygen Studio menu and search 0502 Generic I2C Communication. Description of examples can be found at https://www.rexygen.com/example-projects/0502_Generic_I2C_Communication/0502_Generic_I2C_Communication.html.
Documentation for Rexlang block is here: https://www.rexygen.com/doc/ENGLISH/MANUALS/BRef/REXLANG.html.
Best regards,
Jan -
@reitinge Hello thank you for your time
I appreciate . I'll have a look at these example.
Thx again.Arnaud
-
Hello again everybody.
I just received my I2c components, and I'm really lost to understand how adapt the i2c example for my hardware.
I have understand the line of the rexlang MCP3422 ADC, with i2c handle, adress, ect. But I I don't understand the communication itself.I have as example for my hardware :
With these information I don't understand how I need to adapt the rexlang function made for MCP3422 to match the EZO read/write.
If I understand well, I would say that :
Start is : Open I2C;
I2C adress: is 0x63;
after I don't know how to code the Read or Write function.
In the example of MCP3422, the variable are 2 array [3] for bufTx and bufRx;
and we put 0x88 ( I understand why with the explanation) in the bufTx[0]
and there is a write count of 1 and read of 2.
I think it's this part that I need to adapt ?
And stop is close I2C.Does anyone can help me to understand how to modify the code of an I2C example to match my hardware.
It would be a nice Christmas present for meThanks.
Arnaud.
-
Hi Arnaud,
it's always a little bit tricky to get communication done without testing and remotely, but I'll do my best.At beginning, it is handy to enable Information messages from Rexygen block in the system log:
- Open RexDraw and connect to your Target device (in the menu Target -> Connect or F7 key).
- Click on Target menu again and select Configure System Log.
- Check the Function block messages -> Information box (as marked on attached figure) and hit the OK button.
For I2C communication you should:
-
Firstly, you should open communication channel on specific bus. This is done only once in the init() method in Rexlang with command
i2c_bus_handle = OpenI2C(deviceName);
, where deviceName is name of the bus you want to communicate on. For example /dev/i2c-1:
i2c_bus_handle = OpenI2C("/dev/i2c-1");
Please check, if communication was established successfuly. You can do that with the Trace() command:
Trace(traceID,value);
For example:
Trace(1,i2c_bus_handle);
If you run your script, you should see in the Log something like this:
The 0001 value is the traceID and 1 (0x1) is value of i2c_bus_handle. If i2c_bus_value is negative, something went wrong. -
Next step is setting address of your device (0x63 for you). This step is done only once too in the init() method:
i2c_chip_address = 0x63;
-
You have to prepare data which you want to send. Use the i2c_bufTx array variable which is prepared in the MCP3422 example. For example on this image
you should set following values to i2c_bufTx array:i2c_bufTx[0] = 1; i2c_bufTx[1] = 57; i2c_bufTx[2] = 46; i2c_bufTx[3] = 53; i2c_bufTx[4] = 54; i2c_bufTx[5] = 48; i2c_bufTx[6] = 0;
Be aware, in the Example the array has length only 3 bytes and you need 7 bytes. So enlarge the array in Declaration variables section. -
Use the I2C() method. This method will send and receive data in one step. You can modify the main() method of MCP3422 example as follow:
i2c_bufTx[0] = 1; i2c_bufTx[1] = 57; i2c_bufTx[2] = 46; i2c_bufTx[3] = 53; i2c_bufTx[4] = 54; i2c_bufTx[5] = 48; i2c_bufTx[6] = 0; i2c_write_count = 7; // you are sending 7 bytes i2c_read_count = 5; // you are expecting receive 5 bytes long (you probably need to modify it!!!) //Sending data via I2C i2c_ret_fun = I2C(i2c_bus_handle, i2c_chip_address, i2c_bufTx, i2c_write_count, i2c_bufRx, i2c_read_count);
The answer of your device should be stored in the i2c_bufRx array now. Make sure you read the correct number of bytes and have a long enough array.
Try some easy write/read command first and let me know, if it works!
Cheers,
Jan -
Hello again thx for your time.
When I try to add trace function, I have an error in the during compiling : function 'trace' is not declared ?
The example that you attach in your response, is when you request data from device. ( so we read data )But if I want to write I have this data :
So Do I have to generate an array of 5 and send Start, I2c address, write, "the wanted function", stop ?
And easy test for me is to make a led blinking on the EZo chip :If I understand their example, I need to WRITE find in the EZO and there is no returns values because it's making a led blinking.
The example with ascii code return, is when I do a reading of the probe for example.
My question is if I write Find, Do I have to send ( write; find ) And I alerady have the start, with open I2c and adresse, and stop or do I have to send all data ?Hope I'm enough clear ?
Arnaud.
-
Trace() function has capital T. Find Rexlang documentation for details (https://www.rexygen.com/doc/ENGLISH/MANUALS/BRef/REXLANG.html#x270-26900015).
The start and stop of each message is generated automatically with the I2C command. In main() method, you should need to specify only i2c_bufTx array and number of bytes from this array, which will be send. You can try following for Find example:
i2c_bufTx[0] = 70; //F i2c_bufTx[1] = 105; //i i2c_bufTx[2] = 110; //n i2c_bufTx[3] = 64; //d i2c_write_count = 4; i2c_read_count = 0; //nothing to read //Sending data via I2C i2c_ret_fun = I2C(i2c_bus_handle, i2c_chip_address, i2c_bufTx, i2c_write_count, i2c_bufRx, i2c_read_count);
The init () method does not change, nor does the exit() method.
-
@belgacom I'm sorry, decadic ASCII value for "d" is 100, not 64.
i2c_bufTx[3] =100; //d
-
I do have a negative result with the trace function .
here is a print screen of the log and the current BF code :
What could be the error to have the trace at -106.
Arnaud
-
Error -106 means Invalid parameter. Error codes are documented here: https://www.rexygen.com/doc/ENGLISH/MANUALS/BRef/BRef_ENGap3.html
- Please, delete the space between Trace and (.
- Check value of the parameter p0 in the Rexlang block (attached figure).
This is the value which is set into the variable i2c_dev. In my case, it is the bus /dev/i2c-1. This depends on your wiring. - Are you sure, you are running the Rexygen program on your Raspberry? The Error: REXLANG open this file type is not possible on this platform indicate, that you might run on your local PC and there is no I2C bus.
Cheers,
Jan -
Hello, Happy new year. Thanks for your help and sorry for the time lost for my com issue. I was targeting Localhost and not my Pi. Except the fact that command need to be send in Hex and not in Dec, your code works find.
I'm able to send command and read values back.
Thanks a lot.I still have a couple of question for you :
-
now I have the reading of my probe in bufRx [1] to [5] in DEC. and I need to convert it in ASCII. (51 46 56 57 54 = 3.896 )
Is there a function for that, in Rexlang or another FB to use ? -
Between a write and a read, I have for example 900ms to wait . So currently I have done 2 Rexlang, 1 to write, 1 to read.
And I use a timer between both FB. Is there a way to do it directly in Rexlang, or do I have to continue to use 2 block ?
I didn't find answer in the doc.
Have a good day.
Arnaud.
-
-
@belgacom
Happy new year to you too. You are welcome. I'm sorry for decadic <-> hexadecimal converting trouble. I usually send values as a hex, but I thought the decimal values would work as well.- For converting DEC/HEX values into string, you can use something like this:
int i; char message[5]; /*bufRx[1] = 51; bufRx[2] = 46; bufRx[3] = 56; bufRx[4] = 57; bufRx[5] = 54;*/ for (i=1; i<6; i++) message[i-1] = bufRx[i]; Trace(1,message);
- Yes, you can use one Rexlang block for reading / writing. The easiest way is to use some type of simple if-else or switch-case structure in the main() function. Rexlang provides functions as Sleep() or Suspend() (see documentation https://www.rexygen.com/doc/ENGLISH/MANUALS/BRef/REXLANG.html#x270-26900015) but I think the right way is to send a request and periodically checking if the answer received. You should change the period of your task where the Rexlang block is too. https://www.rexygen.com/doc/ENGLISH/MANUALS/BRef/EXEC.html The Rexlang block source code (and all others blocks in the task) are executed only once per period time.
Best regards,
Jan - For converting DEC/HEX values into string, you can use something like this:
-
Hello, Happy new year. Hope you are going .
I tried to use trace with you code up here, but even if the block is returning the correct value of my Ezo components,
I have an 508 error.
therefore no trace is seen in the log . any idea why 508 error ?Anyway even if I hand write 5 values into message[5], how after do I see it on the HMI page.
If I use a declaration of output like :long output(x) message; or string output(x) I can't manage to see values in message.
How do I proceed ?thx
Arnaud
-
Hi Arnaud,
thanks, I hope you are fine too. Can you post here your actual code from Rexlang, please? When an error occurs, code execution stops, so the outputs may not be updated. We will try to find and fix the Numeric range check error.Cheers,
Jan -
Hi.
I had time to dig a littl bit, and I find solution to do the write and read in 1 rexlang.
After that no more issue with alarm and I'm able to trace " message"It works fine.
But still I don't understand how to display the value of "message" in the HMI designer.I tried with standard display and string display.
I would like also to trend the ASCII value ( 3.xxxx)
Can you give me a hand on this ?Thx.
Arnaud.
my code :
/[5]*************************************
*- REXLANG - Reading data from MCP3422 AD converter via I2C
*************************************************************/
string parameter(0) i2c_dev; // the I2C bus is defined by the p0 parameter
//assigning variables to outputs, these variables are WRITE-ONLY
long output(0) adc_value;
long output(1) adc_value1;
long output(2) adc_value2;
long output(3) adc_value3;
long output(4) adc_value4;
long output(5) adc_value5;
long output(6) adc_value6;
long output(7) message;
string output(8) message;//declaration of variables
long i2c_bufTx[4]; //buffer for transmitting data
long i2c_bufRx[7]; //buffer for receiving data
long i2c_bus_handle;
long i2c_chip_address;
long i2c_write_count;
long i2c_read_count;
long i2c_ret_fun;
int i;
char message[5];//the init procedure is executed once when the REXLANG function block initializes
int init(void)
{
i2c_bus_handle = OpenI2C(i2c_dev); // open I2C bus
Trace(1,i2c_bus_handle);i2c_chip_address = 0x63; // 7-bit address of the I2C device
return 0;
}
//the main procedure is executed once in each sampling period
long main(void)
{
//i2c_bufTx[0] = 0x46;
//i2c_bufTx[1] = 0x69;
//i2c_bufTx[2] = 0x6E;
//i2c_bufTx[3] = 0x64;i2c_bufTx[0] = 0x52;
// i2c_bufTx[0] = 0x53;
//i2c_bufTx[1] = 0x6C;
//i2c_bufTx[2] = 0x65;
//i2c_bufTx[3] = 0x65;
//i2c_bufTx[4] = 0x70;i2c_write_count = 1;
i2c_read_count = 0;//Sending data via I2C
i2c_ret_fun = I2C(i2c_bus_handle, i2c_chip_address, i2c_bufTx, i2c_write_count, i2c_bufRx, i2c_read_count);Suspend (3);
i2c_write_count = 0;
i2c_read_count = 7;i2c_ret_fun = I2C(i2c_bus_handle, i2c_chip_address, i2c_bufTx, i2c_write_count, i2c_bufRx, i2c_read_count);
adc_value = (i2c_bufRx[0]);
adc_value1 = (i2c_bufRx[1]);
adc_value2 = (i2c_bufRx[2]);
adc_value3 = (i2c_bufRx[3]);
adc_value4 = (i2c_bufRx[4]);
adc_value5 = (i2c_bufRx[5]);for (i=1; i<6; i++)
message[i-1] = i2c_bufRx[i];
Trace(1,message);return 0;
}//the exit procedure is executed once when the task is correctly terminated
// (system shutdown, downloading new control algorithm, etc.)
long exit(void)
{
if(i2c_bus_handle>=0) Close(i2c_bus_handle); // close I2C bus
return 0;
} -
This post is deleted! -
@belgacom
Hi Arnaud,
congratulations to your first received message!Your problem with publishing message outside of Rexlang is due to the names of your variables. Output 7, 8 and variable message have the same name and this is a problem. Try something like this:
- Rename outputs:
double output(7) outMessageDouble; string output(8) outMessageStr;
- In the main() method, assign message variable value to output(8) and you can also convert the string value into double and assign it into output(7):
outMessageStr = message; outMessageDouble = str2double(message);
Please resize message variable length from 5 to 6. In the Rexlang, the last char array index contains "end-of-string indicator". If you write something to the last index of the char array, you will have a memory leak problem.
Cheers,
Jan -
Everything works fine !
Thank you. -
@belgacom You are welcome