Modbus calls returning same frame over and over again - c

I'm having a dickens of an issue with this one.
I have a remote installation with a raspberry pi installed in it (specifically a Kunbus RevPi connect) that is hooked up to an RS485 Modbus network with a total of 6 sensors on it. 4 Of these sensors are Dat3018 devices which serve to digitize thermocouples and make their values available over the network.
I wrote a C program that performs the following:
Sends a sync command (specified in the data sheet) to the dat3018 which causes them all to read values into their internal memory
Perform some other stuff
Read the saved values in to the raspberry pi over Modbus.
All is well and good, and I'm able to talk to each of the devices, no issues as far as I can tell on the physical communication link. The issue appears when I actually run the software and observe the Modbus request and response.
The first loop of the program runs normally, the sync command goes out, a request and response is received from each of the target slaves on the network. Starting with the second run of the loop each request results in the program receiving the exact same frame it did during the first run of the loop even through there should be at least some values changing. This continues for however long the program runs, always sending the same request from the server and always receiving the same values back.
As per the data sheet for the Modbus devices the following command is sent:
[FF][06][00][00][00][0A][1C][13]
I verified with the company that makes the device that this was the right call to request the device to sync. I also verified that calling this again will cause new values to be read into the synced value registers, meaning that when I read them again there is a very low chance that they are all exactly the same.
I send the following modbus command with the following code:
int read_tempature_synced_values(modbus_t *RTU_context, int slave_addr, uint16_t output[]){
modbus_set_slave(RTU_context, slave_addr);
return modbus_read_registers(RTU_context, _temp_sync_tempatures_start_addr-1, 8, output); //Decrement to account for the inherient +1 from the library
}
For slave # 20:
[14][03][00][16][00][08][A7][0D]
Restarting the program pulls in new values for the first run of the loop before having the same issue.
I wrote a python script to call the sync command and pull in all the measurements from the same registers and that script gives me changing values as I make more calls. That would seem to indicate to me that the issue is somewhere in the Modbus code.
The part of the code that calls the above function looks like this:
//Read in the temperature measurements
for (int i = 0; op_args->settings_instance->tempatureSlaveAddr[i] != -1; ++i){
uint16_t *raw_measures = malloc(8 * sizeof(uint16_t));
read_tempature_synced_values(op_args->modbus_instance, op_args->settings_instance->tempatureSlaveAddr[i], raw_measures);
usleep(op_args->settings_instance->delay_ms);
for (int u = 0; u <= 7; ++u){
if(valid_sensors[i][u] == FALSE){ //Check that there is a valid sensor for this measurement
measurements[measurements_taken].TYPE = TEMPATURE_MEASUREMENT_TYPE;
measurements[measurements_taken].bankNumber = i;
measurements[measurements_taken].sensorNumber = u;
measurements[measurements_taken].measurementTime = sync_time;
measurements[measurements_taken].tempatueReading = ((float)raw_measures[u])/10;
measurements_taken += 1;
}
}
free(raw_measures);
modbus_flush(op_args->modbus_instance);
}
It seems to me that there is something that is remembering what the responses to the calls were and then bouncing them back to me. I can't see any way that the code I have wrote could be doing this.
One other strange thing:
When running my code on the raspberry pi (and only on the pi) my program will randomly print out the two following lines at the start and then at seemingly random times:
ERROR Connection timed out: select
ERROR Connection timed out: select
This happens before the loading/connection to the serial port or sqlite database that I am using. Googling this issue did not appear to give me anything that makes sense in the context of where it is occuring.
Any help would be greatly appreciated
Edit -> The sync command is run at the start of every loop

I verified with the company that makes the device that this was the right call to request the device to sync. I also verified that calling this again will cause new values to be read into the synced value registers, meaning that when I read them again there is a very low chance that they are all exactly the same.
That's how the device works. The SYNC command tells the device(s) to latch the values into a register.
When you do the read, the devices return the latched value and not the "current" value.
From https://www.datexel.com/datexel-user-guides/dat3018-thermocouple-input-to-rs485-modbus.pdf we have:
SYNCHRONISM
The Synchronism function is performed by a command sent to all devices connected on the net. When the devices receive the Sync command, all
input states are saved in the relative register, to be read after time. Doing this, it is possible to read the value of all inputs at the Sync command time.
To send the synchronism command, write the value 10 in the “Test” register (40001) at the address 255.
NOTE: The sync values are not saved in EPROM.
So, for every new value you want you have to send a new SYNC command:
while (1) {
send SYNC command
for (x in all_devices)
read(x)
}

Related

STM32 Interrupt driven UART receival fails after several flawless receives

Please note the clarification and update at the end of the post
TL;DR: An STM32 has 3 UART connections, 1 for debugging and 2 for actual communication which use the interrupt-driven HAL_UART_Receive_IT. Initially, interrupt driven UART receive works fine, though over time the receive callback for one of the UARTs fires less and less until eventually the STM32 doesn't receive any packets on that one UART at all (despite me being able to verify that they were sent). I suspect the issue to be timing related.
Situation: As part of my thesis, I developed a novel protocol which now has to be implemented and tested. It involves two classes of actors, a server and devices. A device consists of an STM32, ESP32 and a UART to Ethernet bridge. The STM32 is connected via UART to the bridge and via UART to the ESP32. The bridge connects the STM32 to the server by converting serial data sent by the STM32 to TCP packets which it forwards to the server (and vice versa). The ESP32 receives framed packets from the STM32, broadcasts them via BLE and forwards all received and well-formed BLE packets to the STM32. I.e. the ESP32 is just a BLE bridge. The server and ESP32 seem to be working flawlessly.
In a nutshell, the server tries to find out which devices D_j can hear BLE advertisements from device D_i. The server does that by periodically iterating over all devices D_1, ..., D_n and sends them nonces Y_1, ..., Y_n encrypted as X_1, ..., X_n. Upon D_i receiving X_i, it decrypts it to get Y_i, which it then forwards to the ESP32 to be broadcasted via BLE. Conversely, whenever the STM32 receives a packet from the ESP32 (i.e. a packet broadcasted via BLE), it extracts some data, encrypts it and forwards it to the server.
After the server has iterated over all devices, it looks at all the messages it received during that round. If it e.g. received a message with value Y_i sent by D_j, it can deduce that D_i's broadcast somehow arrived at D_j.
Problem: The way I have it set up right now, each STM32 seems to occasionally "miss" messages sent by the ESP32. The more such devices I have in my setup, the worse it gets! With just two devices, the protocol works 100% of the time. With three devices, it also seems to work fine. However, with four devices the STM32's UART receive callback for the ESP32 works fine initially, but after a couple of such rounds it doesn't trigger all the time until eventually it doesn't trigger at all.
Visualization:
The below picture shows a sample topology of n devices. Not drawn here, but if e.g. D_1 was to receive Y_2, it would encrypt it to X_2' and send it across the bridge to the server.
N.B.:
Encryption and Decryption each take ca. 130ms
Average one way delay for one ESP32 receiving packet, broadcasting it and another ESP32 receiving is ca. 15ms
I am aware that UART is not a reliable protocol per se and that one should use framing in a real setting. Nevertheless, I was instructed to just assume that UART is perfect and doesn't drop anything.
Due to the larger scope of the project, using an RTOS is not an option
Code:
#define LEN_SERVER_FRAMED_PACKET 35
#define LEN_BLE_PACKET 24
volatile bool_t new_server_msg;
volatile bool_t new_ble_msg;
byte_t s_rx_framed_buf[LEN_SERVER_FRAMED_PACKET]; // Receive buffer to be used in all subsequent Server send operations
ble_packet_t ble_rx_struct; // A struct. The whole struct is then interpreted as uint8_t ptr. when being sent to the ESP32 over UART
Init:
< set up some stuff>
err = HAL_UART_Receive_IT(&SERVER_UART, s_rx_framed_buf, LEN_SERVER_FRAMED_PACKET);
if (!check_success_hal("Init, setting Server ISR", __LINE__, err)){
print_string("Init after Signup: Was NOT able to set SERVER_UART ISR");
}else{
print_string("Init after Signup: Was able to set SERVER_UART ISR");
}
err = HAL_UART_Receive_IT(&BLE_UART, &ble_rx_struct, LEN_BLE_PACKET);
if(!check_success_hal("Init, setting BLE ISR", __LINE__, err)){
print_string("Init after Signup: Was NOT able to set BLE_UART ISR");
}else{
print_string("Init after Signup: Was able to set BLE_UART ISR");
}
Main loop:
while (1)
{
// (2) Go over all 3 cases: New local alert, new BLE message and new Server message and handle them accordingly
// (2.1) Check whether a new local alert has come in
if (<something irrelevant happens>)
{
<do something irrelevant>
}
// (2.2) Check for new ble packet. Technically it checks for packets from the UART to the ESP32.
if (new_ble_msg)
{
new_ble_msg = FALSE;
int ble_rx_type_code = ble_parse_packet(&ble_rx_nonce, &ble_rx_struct);
HAL_UART_Receive_IT(&BLE_UART, &ble_rx_struct, LEN_BLE_PACKET); // Listen for new BLE messages.
<compute some stuff, rather quick> server_tx_encrypted(<stuff computed>, &c_write, "BLE", __LINE__); // Encrypts <stuff computed> and sends it to the server using a BLOCKING HAL_UART_Transmit(...).
// Encryption takes ca. 130ms.
}
// (2.3) Check for new server packet
if (new_server_msg)
{
new_server_msg = FALSE; // Set flag to false
memcpy(s_wx_framed_buf, s_rx_framed_buf, LEN_SERVER_FRAMED_PACKET); // Copy from framed receive buffer to framed working buffer.
// This is done such that we can process the current message while also being able to receive new messages
HAL_UART_Receive_IT(&SERVER_UART, s_rx_framed_buf, LEN_SERVER_FRAMED_PACKET); // Listen for new server messages.
<decrypt it, takes ca.130 - 150ms. results in buffer ble_tx_struct>
err = HAL_UART_Transmit(&BLE_UART, ble_tx_struct,
LEN_BLE_PACKET, UART_TX_TIMEOUT);
check_success_hal(err); // If unsuccessful, print that to debug UART
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
UART receive callback function:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &SERVER_UART)
{ // One should technically compate huart -> Instance, but that works aswell...
new_server_msg = TRUE;
print_string("UART Callback: Server ISR happened!\r\n"); // Blocking write to debug UART. I know that this is typically considered bad form,
// but as the callback function is only called once per receive and because that's the only way of letting me know that the callback has occurred,
// I chose to keep the print in.
}
else if (huart == &BLE_UART)
{
new_ble_msg = TRUE;
print_string("UART Callback: BLE ISR happened!\r\n");
}
else
{
print_string("UART Callback: ISR triggered by unknown UART_HandleTypeDef!\r\n");
}
}
What I have tried so far:
I wrote a client implementation in Go and ran it on my computer, where clients would just directly send UDP messages to each other instead of BLE. As that version functioned flawlessly even with many "devices", I am confident that the problem lies squarely at the STM32 and its STM32 <-> ESP32 UART connection.
To get it working with 3 devices, I simply removed most of the debugging statements of the STM32 and made the server wait 250ms between sending X_i to D_{i} and X_{i + 1} to D_{i + 1}. As this seems to have at least made the problem so infrequent that I haven't noticed it anymore, I reckon that the core issue is timing related.
Through drawing execution traces, I have already found an inherent weakness to my approach: if an STM32 calls HAL_UART_Receive_it(&BLE_UART, ble_rx_buf, LEN_BLE_PACKET) while the ESP32 is currently transmitting a packet to the STM and has already sent k bytes, the STM32 will only receive LEN_BLE_PACKET - k bytes. This causes the BLE_UART.RxXferCount to be wrong for when the next packet is sent by the ESP32.
On a more theoretical front, I first considered doing DMA instead of interrupt driven receive. I then refrained however, as in the STM32 DMA doesn't use descriptor rings like in more powerful systems but instead really just removes the overhead from having to receive LEN_BLE_PACKET (resp LEN_SERVER_FRAMED_PACKET) interrupts.
I have also already of course checked stackoverflow, several people seem to have experienced similar issues. E.g. UART receive interrupt stops triggering after several hours of successful receive, "Uart dma receive interrupt stops receiving data after several minutes" .
Questions:
Given what I have described above, how is it possible for the STM32's callback of BLE_UART to simply stop triggering after some time without any apparent reason?
Does it seem plausible that the issue I raised in the last paragraph of "What I have tried so far" is actually the cause of the problem?
How can I fix this issue?
Clarification:
After the server sends a request to a device D_i, the server waits for 250ms before sending the next request to D_{i + 1}. Hence, the D_i has a 250ms transmission window in which no D_j can transmit anything. I.e. when it's D_i's turn to broadcast its nonce, the other devices have to simply receive one UART message.
As the receival from the server is typically rather fast, the decryption takes 130ms and the UART transmit with a baud of 115200 is also quick, this window should be long enough.
UPDATE:
After posting the question, I changed the ESP32 such that BLE packets are not immediately forwarded over UART to the STM32. Instead, they are enqueued and a dedicated task in the ESP32 dequeues them with a minimum 5ms delay between packets. Hence, the STM32 should now have a guaranteed 5ms between each BLE packet. This was done to reduce the burstiness (despite there not actually being any bursts due to what is mentioned in the clarification... I was just desperate). Nevertheless, this seems to have made the STM32 "survive" for longer before the UART receiver locking up.
You need to be very careful especially when using STM32 HAL library for production, the libraries isn't reliable when receiving fast and continuous data from the server or anywhere else.
I will suggest a solution to this problem based on what I did when implementing for similar application. This works well for my Firmware-Over-The-Air(FOTA) project and helps to eliminate any possible UART failures when using STM32 HAL library.
Steps are listed below:
Ensure you reset the UART by calling MX_USARTx_UART_Init()
Reconfigure the callback either for HAL_UART_Receive_IT() or HAL_UART_Receive_DMA()
This two settings would eliminate any UART failure for receive interrupt using STM32 HAL.

How to send data over serial port without Operating system in c minimally?

I am trying to debug an hyper-visor I am writing, and I set up a serial port between two virtual machine in Vmware player(I am testing the hyper visor on a virtual machine with nested vt-x support). My question is how can I transfer data from the hyper visor to the serial port without the OS help(most of the software stack OS is not available when the hyper visor is on). I know I can see how other systems implement sending to a serial port and implement that way, but this beat the point of debugging because implementing a complete interface to serial port might take more time than finding the bugs manually. I only need to send one number, or a string so I am looking for the minimalist solution possible.
Until know I have been using netconsole in order to pass debugging information, but the bug I am facing right now, happens before the kernel get back the control, so netconsole is not able to send any data. If anyone have a better idea on how to pass the debugging information other than using serial port I would love to hear that.
Right now I have came into that Resource https://en.wikibooks.org/wiki/Serial_Programming/8250_UART_Programming , that might be relevant to my question, but I would love if someone can elaborate, sending data through using out, is working, but the data order is messed up and cant be used for debugging, so, so far it dosent help.
It is quite easy to set up and use a serial port for diagnostic output, and I predict that you will find it immensely useful. To start with, you can use it just for debug output, but since it can be bidirectional, you can extend it as needed to do such things as dump and modify memory and registers (both hypervisor state and guest state).
To set up a serial port, first you have to find the base address. If it is COM1, then it is 3f8, so I'll use that as an example. To initialize the port, write these values, using the OUT instruction:
3fb = 80 // LCR = DLAB
3f8 = 1 // DLL = divisor low (115200 baud)
3f9 = 0 // DLM = divisor high
3fb = 3 // LCR = 8/1/n
3f9 = 0 // IER = 0
3fa = 1 // FCR = FIFO enable
3fc = 3 // MCR = terminal ready | request to send
To send a character, read from 3fd (LSR) using the IN instruction and loop until bit 5 is set (THR empty). Then write the character to 3f8 (THR).
Of course, a serial port driver can be much more sophisticated than this, but this is enough to get it going.

creating a serial loopback under VxWorks

I'm fairly new to VxWorks OS and hence wouldn't mind explanations in case I differ in my understanding of things under the hood when they differ from more traditional OSes like Linux and the likes. With that out of the way, let me begin my actual question.
I am trying to create a loop-back test for testing changes I made to the serial UART driver on the board. Since I do not want to use a cross cable to actually short two UART ports externally, I've connected both of those ports to my dev machine. One configured as an output port from the dev machine's perspective (and consequently as an Input port on the board) and the other an input port (an output port on the board). I am actually doing the loopback using a shared memory buffer which I am protecting using a mutex. So there are two threads on the board, one of which reads from the input port, copies data to the shared memory and the other reads from the memory and sends it over the output port.
And I am using regular open, read and write calls in my VxWorks application (by the way I think it is part of the application code as I call the functions from usrAppInit.c not withstanding the fact that I can even call driver routines from here! (Is it because of a flat memory model vis-a-vis Linux?? Anyhow).
Now I these ports on VxWorks have been opened in a non blocking mode and here's the code snippet which configures one of the ports:
if( (fdIn = open(portstrIn, O_RDONLY | O_NOCTTY, 0) ) == ERROR) {
return 1;
}
if(((status = ioctl(fdIn, FIOSETOPTIONS, OPT_RAW))) == ERROR)
{
return 1;
}
/* set the baud rate to 115200
*/
if((status = ioctl(fdIn, FIOBAUDRATE, 115200)) == ERROR)
{
return 1;
}
/* set the HW options
*/
if((status = ioctl(fdIn, SIO_HW_OPTS_SET, (CS8 | 0 | 0))) == ERROR)
{
return 1;
}
And similarly the output port is also configured. These two are part of two separate tasks spawned using taskSpawn and have the same priority of 100. However what I am annoyed by, is that when I write to the in port from my dev machine (using a python script), the read call on the board get's sort of staggered (I wonder if that's the right way to refer to it). It is most likely due to the short availability of hardware buffer space on the UART input buffer (or some such). This is usually not much of a problem if that is all I am doing.
However, as explained earlier, I am trying to copy the whole received character stream into a common memory buffer (guarded by a mutex of course) which is then read by another task and then re-transmitted over another serial port (sort of an in memory loopback if you will)
In lieu of that aforementioned staggering of the read calls, I thought of holding the mutex as long as there are characters to be read from the Inport and once there are no chars to be read, release the mutex and since this is VxWorks, do an explicit taskDelay(0) to schedule the next ready task (my other task). However since this is a blocking read, I am (as expected) stuck on the read call due to which my other thread never gets a chance to execute.
I did think of checking if the buffer was full and then doing the explicit task switch however if any of you have a better idea, I'm all ears.
Also just to see how this staggered read thing works from the perspective of the kernel, I timed it using a time(NULL) call just before and right after the read. So surprisingly, the very first chunk shows up a number, every other chunk after that (if it's a part of the same data block coming from the outside) shows 0. Could anyone explain that as well?
Keen to hear
I don't have 50 rep points for commenting, but without a loopback cable attached, the only way you can arrive at testing serial loopback behavior is to switch the uart into loopback mode. This often means making changes to the specific hardware part driver.

Why does SPI.transfer() freeze the second time I call it?

I'm trying to learn how to get an Arduino to talk to an XBee PRO S3B module via SPI. As sort of a hello world, I figured I would try to read some information about the XBee module's configuration (specifically, its DL setting). Reading this information requires first sending an 8-byte length command through SPI.
I use the SPI.transfer() function of the Arduino SPI library to send the command one byte at a time. It looks like I am able to send the first byte just fine -- the problem is that the code seems to freeze on the second time I call SPI.transfer().
In my code, I have the data of the command stored in a byte array called AT_getDL_CMD, whose length (8) is stored in AT_getDL_LEN. Here is the relevant part of the code:
// start the SPI library:
SPI.beginTransaction(SPISettings(3000000, MSBFIRST, SPI_MODE0));
// initalize the pins:
pinMode(SPI_ATTN, INPUT);
pinMode(SPI_SSEL, OUTPUT);
digitalWrite(SPI_SSEL, LOW);
// wait a little bit for the radio to be ready:
delay(1000);
Serial.println("Sending getDL API command");
for (int i = 0; i < AT_getDL_LEN; i++) {
Serial.print(SPI.transfer(AT_getDL_CMD[i]));
Serial.print(", ");
}
The output I get from this is:
Sending getDL API command
0,
So it looks like the loop runs for one iteration but then freezes when it calls SPI.transfer() on the second iteration.
What could be causing this? Is it more likely to be a problem on the Arduino's end, or on the XBee's end? Am I using the SPI library incorrectly?

serial device ignores EscapeCommFunction with C

I am migrating (finally) from MSDOS to Windows XP for controlling a meter via the serial port. My old C DOS code works fine.
I want to do as follows:
meter is continuously taking readings
every few sec, but does not send any
info until it is requested by the
computer
when computer is ready to receive
info from meter, it requests it. It does not accept info otherwise.
My problem is that the readings are just coming into the computer as they are generated by the meter.
I have set the DCB serail params as follows, intending to control communication using RTS and DTR:
dcbSerialParams.BaudRate=CBR_4800;
dcbSerialParams.ByteSize=7;
dcbSerialParams.StopBits=TWOSTOPBITS;
dcbSerialParams.Parity=EVENPARITY;
dcbSerialParams.fDtrControl=DTR_CONTROL_ENABLE;
dcbSerialParams.fRtsControl=RTS_CONTROL_ENABLE;
My old code under DOS was like this:
outportb(COM1+4,0x03); /* start Minolta reading */
for(j=0;j<=10;j++) /*each reading consists of 11 ascii characters*/
{
while(!((inportb(COM1+5)) & 1)); /*wait until char received*/
reading[j]=inportb(COM1);
}
sscanf ( &reading[4], "%f", &lum[k] );
outportb(COM1+4,0x00); /* stop Minolta reading */
It seems to me that this should work:
void serial_notready(void)
{
EscapeCommFunction(hSerial,CLRDTR);
EscapeCommFunction(hSerial,CLRRTS);
}
void serial_ready(void)
{
EscapeCommFunction(hSerial,SETDTR);
EscapeCommFunction(hSerial,SETRTS);
}
int serial_read(char reading[])
{
DWORD dwBytesRead = 0;
int nbytes=11;
ReadFile(hSerial, reading, nbytes, &dwBytesRead, NULL);
return(dwBytesRead);
}
serial_ready(void);
x = 0; while(x == 0){x=serial_read(reading);}
serial_notready(void);
HOWEVER, the Minolta does not wait to receive the RTS from the computer. It just goes ahead and sends readings as each becomes available. At the same time, the computer does not reject any unwanted reading, but accepts it.
I have been bashing my head against the wall trying to figure this out, trying all kinds of permutations to no avail. Any help greatly appreciated!
Update:
The underlying story is that I present a given luminance (brightness) on a display and then need the corresponding luminance reading. This is done for a whole set of luminances.
L ---
U ---
M ---
TIME
I present lum1, lum2, lum3, lum4,.... If the measurements are not synchronised to the display, then I may get a supposed reading3 that is actually lum2, or some sort of average because the reading crossed the border between the lum2 and lum3 displays. And, as you said, Hans,the readings will always lag behind the display luminances. Even if I were always systematically one reading behind it would be bad (my situation is worse-- it is a a random relation between the reading and the luminance).
So the behaviour of the windows serial routines is a nightmare for me. Thanks again for the help!
dcbSerialParams.fDtrControl=DTR_CONTROL_ENABLE;
dcbSerialParams.fRtsControl=RTS_CONTROL_ENABLE;
You enable the DTR and RTS signals right away. The meter will immediately start sending data when you open the port. That data gets buffered in the driver's receive buffer. You didn't have a buffer before in the DOS code. It depends how long it takes for you to call serial_notready(). You'll have a pretty full buffer if that takes a second or so. Yes, that makes it look like the meter is just sending data. And you are always reading an old sample.
Start with the DCB values set to DISABLE. Beware that the scheme is brittle, you could turn the signal off pretty reliably back in the DOS. Now you've got a driver in between. You may well end up turning RTS off too late. Which risks getting a stale reading. An alternative is to startup a thread that just reads continuously. And have your main code just use the last value that it read. The overhead is quite low, serial ports are slow.
The first thing to do would be to check the return values of the calls to EscapeCommFunction(). If the return value is zero, the call failed and you should use GetLastError() to receive more error information.
I use a free third party serial port emulator VPS. its got a interval request timer that dectate when excatly the data need to be updated/grapped. also allow me to log the bus packets into an excel file.

Resources