Search code examples
pythoncsocketsuser-interfacespi

Python Gui to c app connection with sockets


Not long ago, i've started a project where i sent data from a python gui, to a python controller app, through a socket connection. The controller proceeds to send the data to a peripheral device, through an SPI connection Now, i wish to modify my project and port the controller app into c.

I will provide my code and the problems that i've faced below.

gui code :

def Configure(e):
     
     Command(0, 0, ch0, reg_X, group_0, ch_0, 1)

def dacCommand(bus,dev,dacVar,command,dacGroup,dacChannel,multFac):



  Vref = 3
  offsetCode = 0x1555
  M = 65535
  C= 32768
  Iout=dacVar.get()
  Vout=Iout*multFac

  byte1 = command|dacGroup|dacChannel

  dacCode=math.ceil(Vout*(2**16)/(4*Vref)+4*offsetCode)
  inputCode=math.floor((dacCode-C+(2**15))*(2**16)/(M+1))

  data = struct.pack('>BH', byte1, inputCode)
  client_socket.sendall(data)


if __name__ == "__main__":          

    window = Tk()
    window.title('DACs board control')
    window.geometry('1920x900')
   
    tab_control = ttk.Notebook(        window)
    
    tab2        =         Frame(  tab_control)
    tab_control.add(tab2, text= 'dev1 control')
    
    tab_control.pack(expand= 1,  fill= "both")
    
    ch0 = DoubleVar()
    ttk.Label(tab2, text= "ch0 (V)", font= ("Calibri", 12)).grid(column= 0, row= 0, padx= 10,     
    pady= 5)
    ch0Scale = Scale(tab2, from_= 0, to = Vmax, resolution = 0.05, state = "normal", variable= 
    ch0, command= Configure, font='Helevetica 11', length = 200, width = 15).grid(row = 1, 
    column = 0,  pady = 5, padx = 10, sticky = 'w')
    
    mainloop()

While connecting the gui to the python controller,i've noticed that some values were not sent to the controller. This might have happened because the change was fast and the program couldn't ragister it. Is there some way that i can modify the gui to register the value upon button release ( something like a "buttonreleased" event).

The c controller code is :

#define PORT 1234


int main(int argc, char *argv[])
{
    int server_fd, new_socket, valread;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[3] = {0};
    
    int spi_fd;
    struct spi_ioc_transfer spi_transfer;
    unsigned char rx_buffer[3] = {0};
    
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // Forcefully attaching socket to the port 1234
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    
    // Forcefully attaching socket to the port 1234
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    if (listen(server_fd, 1) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    
    
    // Open SPI device
        spi_fd = open("/dev/spidev0.0", O_RDWR);
        if (spi_fd < 0) 
        {
            printf("Error: Failed to open SPI device.\n");
                return -1;
        }
            
        // Configure SPI mode, bits per word, and max speed
        u_int8_t mode = SPI_MODE_1;
        u_int8_t bits = 8;
        u_int32_t speed = 1000000;
        ioctl(spi_fd, SPI_IOC_WR_MODE, &mode);
        ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
        ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    
    
    while(1)
    {      if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
     }
            valread = read(new_socket, tx_buffer, 3);
          
            
            // Transfer data over SPI
            memset(&spi_transfer, 0, sizeof(spi_transfer));
            spi_transfer.tx_buf = (unsigned long)tx_buffer;
            spi_transfer.rx_buf = (unsigned long)rx_buffer;
            spi_transfer.len = sizeof(tx_buffer);
            spi_transfer.speed_hz = speed;
            spi_transfer.bits_per_word = bits;
            spi_transfer.delay_usecs = 0;
            
            int status = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &spi_transfer);
            if (status < 0) {
                printf("Error: Failed to transfer data over SPI.\n");
                close(spi_fd);
                return -1;
            }
        
        
         printf("Received message: %02x %02x %02x\n", buffer[0], buffer[1], buffer[2]);
        
        
        // Close SPI device
        close(spi_fd);
        
        
        
    }
    close(new_socket);
    return 0;
}

Since i've wanted my connection to be permanent until app termination, i've made it so that it can continuously receive data from the gui, by using a super loop. My connection seem to be okay until i terminate the program, but the controller seems to receive only the first value that i send from the gui

Any suggestion would be welcomed.


Solution

  • I would suggest you change your code to:

    while(1) {
        /* There, you wait for a new client connection. */
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            perror("accept");
            exit(EXIT_FAILURE);
        }
    
        /* You read only 3 data bytes that get stored into tx_buffer. */
        valread = read(new_socket, tx_buffer, 3);
    
        // Transfer data over SPI
        memset(&spi_transfer, 0, sizeof(spi_transfer));
        spi_transfer.tx_buf = (unsigned long)tx_buffer;
        spi_transfer.rx_buf = (unsigned long)rx_buffer;
        spi_transfer.len = sizeof(tx_buffer);
        spi_transfer.speed_hz = speed;
        spi_transfer.bits_per_word = bits;
        spi_transfer.delay_usecs = 0;
    
        int status = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &spi_transfer);
        if (status < 0) {
            printf("Error: Failed to transfer data over SPI.\n");
            close(spi_fd);
            return -1;
        }
    
        printf("Received message: %02x %02x %02x\n", buffer[0], buffer[1], buffer[2]);
    
        /* According to your code, you seem to be done using the client socket
         * here, so you can just close it. */
        close(new_socket);
    }
    
    /* As you use the SPI within the above loop, you cannot close it above. This
     * may lead to un-expected behavior/errors. You should rather close it after
     * you are done using it. */
    close(spi_fd);
    

    NOTE: notice the spi_fd close operation is done after you are done using it ; not at the end of the loop.

    NOTE: I kept the main logic of your code. Your GUI application has to open a connection every time it wants to send a command. This is due to the first line of your loop.

    If you want the connection to be held active, then you should add an other loop which looks like this:

    while(1) {
        /* Wait for a new client connection. */
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            perror("accept");
            exit(EXIT_FAILURE);
        }
    
        /* Client reading loop. Process client's data as long as it is
         * connected. */
        while (1) {
            /* Check loop condition to close the connection. You can use
             * `select` to find out if something was read or if an error
             * occurred). */
            // TODO
    
            valread = read(new_socket, tx_buffer, 3);
    
            // Transfer data over SPI
            memset(&spi_transfer, 0, sizeof(spi_transfer));
            spi_transfer.tx_buf = (unsigned long)tx_buffer;
            spi_transfer.rx_buf = (unsigned long)rx_buffer;
            spi_transfer.len = sizeof(tx_buffer);
            spi_transfer.speed_hz = speed;
            spi_transfer.bits_per_word = bits;
            spi_transfer.delay_usecs = 0;
    
            int status = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &spi_transfer);
            if (status < 0) {
                printf("Error: Failed to transfer data over SPI.\n");
                close(spi_fd);
                return -1;
            }
    
            printf("Received message: %02x %02x %02x\n", buffer[0], buffer[1], buffer[2]);
        }
    
        close(new_socket);
    }
    
    /* As you use the SPI within the above loop, you cannot close it above. This
     * may lead to un-expected behavior/errors. You should rather close it after
     * you are done using it. */
    close(spi_fd);
    

    Take a look at select. It is a non-blocking (although it can also be blocking) way to know the current state of your socket: can it be read, can it be written, has an error occurred? You can then use these to determine whether you want to read from the client, or break the reading loop if it has disconnected.