I'm writing a program in C++ on a Raspberry Pi 4 to command some motors and get back the informations of speed sensors through the GPIO pins.
To do so, there is a main control program with a "fork" in it. The parent process does all the calculation and the child process is constantly sending and receiving data through a SPI communication (sending and receiving data are done at the same time and the library don't allow to do either one or the other).
On each loop, the child reads data sent by the parent (this part works) and it sends back the data read on the inputs (works once) to the parent.
I actually have the "read" data wired with cables so I can control the inputs and be sure of their states.
In the following example, I give the requested infos to the program to have the parent write to the child, which works (choosing motor 0/15, having a speed of 0 and a direction 0=stopped and not stopping the program)
On the first try, it tells me the read data is 10, which is correct.
I then move my jumping cables to another value (15 in this case) but it still gives me 10 on the second try
I checked my program, the write from the child receives the good data, it is really the piping that has a problem and I can't get my head around it. It looks like the Pipe gets closed...
Here is the terminal view
The problem is between the parent lines
if (read(fdCP[0], In0, sizeof(short int)*2) == -1) { std::cout << "Something went wrong in Parent read :'( " <<'\n'; }
and the child lines
//If here I print the content of "In0", it is correct and fitting what the program read
if(write(fdCP[1], In0, sizeof(short int)*2) == -1) { std::cout << "Something went wrong in child write :'( " <<'\n'; }; //sending data through the pipe
And here is the programm stripped down of its vast majority for readability.
#include <bcm2835.h> //SPI & GPIO communication library
#include <stdio.h>
#include <unistd.h> //got fork() in it
#include <sys/wait.h>
#include <vector>
#include <iostream> //for Cout to display stuff
#include <thread> //for multithreading
#include <string.h>
#include <time.h>
using std::vector;
int SRDInit();
char* SendReceiveData(char* SpiOut, char* SpiIn); //Defines the existance of this function and will check in the compiled stuff if it exists (spoiler : it does)
int SRDClose();
int main(int MotNb, int Spd1, int Dir1)
{
//Fork and multiprocessing stuff
bool EndThis = false; //Variable for ending the continous look of sending and receiving data from shift registers
//SPI Communication buffers and data storages
char SpiOut[2]={0,0}; //Defining the char to send by SPI and set it to 0
short int In0[2] = {0}; //Char array for receiving data
int Sp1 = 0; // iteration variable for speed
short int SpdDir[4] = {0, 0, 0, 0}; //creates a table with 4 ints inside 0 for motor number, 1 for Speed, 2 for Dirrection and 3 for killing the child
//###################################################################################
//Actual beginning of the program
//###################################################################################
int fdPC[2]; //File descriptor for the piping Parent to child
int fdCP[2]; //File descriptor for the piping Child to Parent
std::cout << "Init FD" << '\n';
if (pipe(fdPC) == -1){ //creating the pipe for communication with the second program sending and receiving data
//fd[0] = Read fd[1]=write
std::cout << "an error occured while creating the pipe Parent to Child" << std::endl;
return 1;
}
if (pipe(fdCP) == -1){ //creating the pipe for communication with the second program sending and receiving data
//fd[0] = Read fd[1]=write
std::cout << "an error occured while creating the pipe Child to Parent" << std::endl;
return 1;
}
std::cout << "Forking" << '\n';
int id = fork(); //forking the program to create a second one playing on another processor
std::cout << "Forked ID = " << id << '\n';
if (id < 0){
std::cout << "An error occured while forking :'(" << std::endl;
}
else if (id > 0){ //if in the main process
std::cout << "Enter Main process" << '\n';
//###################################################################################
//Main process.
//###################################################################################
close (fdPC[0]); //closing the read side of the Parent to child pipe
close (fdCP[1]); //closing the write side of the child to parent pipe
fcntl(fdPC[1], F_SETFL,O_NONBLOCK);
fcntl(fdCP[0], F_SETFL,O_NONBLOCK);
int MotNb = 0; //variable for the number of the motor to be modified
short int OldIn0[2] = {0, 0};
short int NewIn0[2] = {0, 0};
while(EndThis == false){
std::cout << "Gi'me Motor 0-15" <<'\n';
std::cin >> MotNb;
std::cout << "Gi'me speed" <<'\n';
std::cin >> Spd1;
std::cout << "Gi'me dirrection 0=3= stop 1=fwd 2=bwd" <<'\n';
std::cin >> Dir1;
std::cout << "Stop that? Y/N" <<'\n'; //check if we want to close the program
std::cin >> EndProg;
SpdDir[0] = MotNb; //storing the Motor number
SpdDir[1] = Spd1; //storing the speed
SpdDir[2] = Dir1; // storing the dirrection
if(write(fdPC[1], SpdDir, sizeof(short int)*4) == -1) { std::cout << "Something went wrong in Parent write :'( " <<'\n'; } //Writing through the pipe to send data to the Child
if (read(fdCP[0], In0, sizeof(short int)*2) == -1) { std::cout << "Something went wrong in Parent read :'( " <<'\n'; }
NewIn0[0] = In0[0];
NewIn0[1] = In0[1];
if (NewIn0[0] != OldIn0[0] || NewIn0[1] != OldIn0[1]) {
std::cout << "SOMETHING CHANGED !!! Old In0 = " << +OldIn0[0]<< +OldIn0[1] << " and NewIn0 = " << +NewIn0[0]<< +NewIn0[1] << '\n';
OldIn0[0] = NewIn0[0];
OldIn0[1] = NewIn0[1];
}
if (SpdDir[3] > 0){
EndThis = true;
std::cout << "Stopping main" <<'\n'; //check if we want to close the program
}
} //End of PARENT while
write(fdPC[1], SpdDir, sizeof(int)*3); //Writing through the pipe to send data to the Child for ending the program
//End of main program, closing everything and freeing memory because we're tidy people
close (fdPC[1]); //closing the write side of the Parent to child pipe
close (fdCP[0]); //closing the read side of the child to parent pipe
if (wait(NULL) == -1){ //wait for the child to finish working == -1
std::cout << "No children to wait for" << std::endl;
}else {
std::cout << "waiting for children to finish" << '\n';
}
}
else{ // if in the secondary process
std::cout << "Enter child process" << '\n';
//###################################################################################
//Child process. Send Speed and dirrection requested and receive data (because no choice for receiving)
//###################################################################################
close (fdPC[1]); //closing the write side of the Parent to child pipe
close (fdCP[0]); //closing the read side of the child to parent pipe
fcntl(fdPC[0], F_SETFL,O_NONBLOCK);
fcntl(fdCP[1], F_SETFL,O_NONBLOCK);
int MotNb = 0; //variable for the number of the motor to be modified
//Actual beginning of child program
char* SpiIn=(char*)malloc(sizeof(char)*17); //creation de la data qui sera échangée malloc est fermé apres l'appel a la fonction
if(SpiIn==NULL){
perror("Char allocation of SendReceiveData FAILED !!");
return 1;
}
while(EndThis == false){
read(fdPC[0], SpdDir, sizeof(short int)*4);//read data from the pipe put them in SpdDir varaible
MotNb = SpdDir[0]; //storing the motor nb
Spd1 = SpdDir[1]; //storing the speed
Dir1 = SpdDir[2]; // storing the dirrection
//Some amazing coding has been deleted here ;)
In0Raw = SendReceiveData(SpiOut, SpiIn); //Send data in "SpiOut" and puts the data received in In0Raw
In0[0] = In0Raw[1]; //Data is no read in the logical direction TBC
In0[1] = In0Raw[0]; //Data is no read in the logical direction TBC
//If here I print the content of "In0", it is correct and fitting what the program read
if(write(fdCP[1], In0, sizeof(short int)*2) == -1) { std::cout << "Something went wrong in child write :'( " <<'\n'; }; //sending data through the pipe
sleep(1);
if (SpdDir[3] > 0){
EndThis = true;
std::cout << "Ending child" << '\n';
}
} //End of CHILD while
//End of child program, closing everything and freeing memory because we're tidy people
close (fdPC[0]); //closing the read side of the Parent to child pipe
close (fdCP[1]); //closing the write side of the child to parent pipe
free(In0Raw); //free memory allocated to In0Raw for "SendReceiveData"
std::cout << "Child ended" << '\n';
exit(EXIT_SUCCESS);
}
//###################################################################################
if (id > 0){
std::cout << "End of program enter a key and press ENTER to finish" <<'\n';
std::cin >> Spd1;
SRDClose();
free(In0Raw); //release memory of the pointer just in case
}
return 0;
}
If you're wondering what "SendReceiveData" is, it's just a very tiny function sending and receiving data through the SPI connection. It works fine, it's really basic ;)
I know where the problem lies, but I have absolutely no idea what the problem is. It looks like the pipe is closed wfter writing it once from Child to Parent, but it works very fine in the other direction, to send instructions from Parent to Child
(If needed, I can copy the whole code instead of this extract)
Thanks by advance for your help ! :)
I've read the documentation (given by a friend) write() manual which did not help, but can be useful to someone.
I tried to understand the error, using errno
for which I checked the documentation errno manual and was useful.
The write() sent back the error "Resource temporarily unavailable" which corresponds to "EAGAIN" but, most importantly it is said "may be the same value as EWOULDBLOCK" corresponding to "Operation would block"
I searched and found a discussion where the answer was "the buffer is full"
So, I asked if it is possible to overwrite a data in the write() file descriptor and the answer is "no" :'( Is it possible to overwrite a write() data?
You have to read a data in a file descriptor before writing again.
Apparently, threads are better for that, I have to invesstigate.
Hoping this helps someone.