element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Members
    Members
    • Benefits of Membership
    • Achievement Levels
    • Members Area
    • Personal Blogs
    • Feedback and Support
    • What's New on element14
  • Learn
    Learn
    • Learning Center
    • eBooks
    • STEM Academy
    • Webinars, Training and Events
    • More
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • More
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • More
  • Products
    Products
    • Arduino
    • Dev Tools
    • Manufacturers
    • Raspberry Pi
    • RoadTests & Reviews
    • Avnet Boards Community
    • More
  • Store
    Store
    • Visit Your Store
    • Choose Another Store
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
Test & Tools
  • Technologies
  • More
Test & Tools
Blog SCPI on a Linux Board - Letter of Intent
  • Blog
  • Forum
  • Documents
  • Events
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Test & Tools requires membership for participation - click to join
Blog Post Actions
  • Subscribe by email
  • More
  • Cancel
  • Share
  • Subscribe by email
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 27 Apr 2018 5:41 PM Date Created
  • Views 824 views
  • Likes 14 likes
  • Comments 21 comments
  • pi
  • bbb
  • BeagleBone
  • raspberrypi
  • scpi
Related
Recommended

SCPI on a Linux Board - Letter of Intent

Jan Cumps
Jan Cumps
27 Apr 2018

I'm trying to build a SCPI shell for Linux.

I'd like to have it working over TCP/IP on a Raspberry Pi and a BeagleBone.

 

tl;dr It works. The rest of the blog series explains how to build a lab automation device that controls 8 outputs (2 of them relays), and read 8 inputs. With a Raspberry Pi and a PiFace Digital (2).

 

 

Here on element14, we've been playing with SCPI and microcontrollers. We have several instruments working on MSP432, Arduino and Hercules devices.

This time I'd like to get a Linux instrument up. To get some device independence, I'll try to make the source work on two different boards with different distros.

The program is expected to run as a background process.

 

I have a head start. My favourite SCPI library has a LwIP SCPI server example.

A relative head start, that is, because I have to find out how to get all the dependencies - I want to develop on a Windows PC .

If this project fails, it'll be because of my develop for Linux skills.

 

related blog
SCPI on a Linux Board - Letter of Intent

SCPI on a Linux Board - Part 1: Proof of Concept

SCPI on a Linux Board - Part 2a: PiFace Digital C programming

SCPI on a Linux Board - Part 2b: PiFace Digital C++ programming
SCPI on a Linux Board - Part 3: TCP/IP Socket C++ programming
SCPI on a Linux Board - Part 4: TCP/IP SCPI and Instrument Service
SCPI on a Linux Board - Part 4b: TCP/IP SCPI and Instrument Service 100% Working
SCPI on a Linux Board - Part 4c: TCP/IP SCPI and Instrument Service - Run as a Daemon
SCPI on a Linux Board - Part 5a: LabVIEW Driver for LAB Switch: Open, Close and Switch functions
Anonymous

Top Comments

  • Jan Cumps
    Jan Cumps over 4 years ago in reply to Jan Cumps +4

    Yay!

     

     

     

     

    It works. Above is the capture of a LabVIEW back and forth communication - with the Raspberry Pi as a VISA TCP/IP device.

    *IDN? command sent, and the reply from the Pi arrived at LabVIEW.

  • DAB
    DAB over 4 years ago +3

    Good luck Jan.

     

    DAB

  • Jan Cumps
    Jan Cumps over 4 years ago in reply to Jan Cumps +3

    what's not working ok:

     

    • even though I get a reply, the Visa Read block in LabVIEW throws a timeout error.
    • when I close a connection (regardless of telnet, LabVIEW), the service on the Raspberry Pi goes…
Parents
  • Jan Cumps
    Jan Cumps over 4 years ago

    Some more progress: first contact from LabVIEW

     

    I don't have the reply working in the LabVIEW flow yet - it works different than serial/usb. But things look good.

    • Cancel
    • Vote Up +2 Vote Down
    • Reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to Jan Cumps

    Yay!

     

     

     

     

    It works. Above is the capture of a LabVIEW back and forth communication - with the Raspberry Pi as a VISA TCP/IP device.

    *IDN? command sent, and the reply from the Pi arrived at LabVIEW.

    • Cancel
    • Vote Up +4 Vote Down
    • Reply
    • More
    • Cancel
  • shabaz
    shabaz over 4 years ago in reply to Jan Cumps

    Hi Jan!

     

    I can't help on the LabView portion, but if you've got the Pi portion of the code somewhere, I'd be happy to examine it in case something sticks out. In terms of general ideas for debugging, you're already using gdb so that's great, it might be worth compiling on the Pi directly once, just to 100% confirm you're linking to the same versions of libraries. Also, 'strace' is sometimes useful too, although it chucks out a lot of debug.

    • Cancel
    • Vote Up +1 Vote Down
    • Reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to shabaz

    The code as it stands now. It's a server side socket program that listens and serves info to a tcp/ip client knocking on its port:

     

    /*
     ============================================================================
     Name        : main.c
     Author      : Jan Cumps
     Version     :
     Copyright   : free for all
     Description :
     ============================================================================
     */
    
    
    /* A simple server in the internet domain using TCP
       The port number is passed as an argument */
    #include 
    #include 
    #include 
    #include 
    
    
    #include "scpi/scpi.h"
    #include "scpi-def.h"
    
    
    int newsockfd;
    
    
    /** SCPI utilities
     *
     */
    
    
    size_t SCPI_Write(scpi_t * context, const char * data, size_t len) {
        (void) context;
        int n = write(newsockfd, data, len);
        if (n < 0) error("ERROR writing to socket");
        return n;
    }
    
    
    scpi_result_t SCPI_Flush(scpi_t * context) {
        (void) context;
    
    
        return SCPI_RES_OK;
    }
    
    
    int SCPI_Error(scpi_t * context, int_fast16_t err) {
        (void) context;
    
    
        fprintf(stderr, "**ERROR: %d, \"%s\"\r\n", (int16_t) err, SCPI_ErrorTranslate(err));
        return 0;
    }
    
    
    scpi_result_t SCPI_Control(scpi_t * context, scpi_ctrl_name_t ctrl, scpi_reg_val_t val) {
        (void) context;
    
    
        if (SCPI_CTRL_SRQ == ctrl) {
            fprintf(stderr, "**SRQ: 0x%X (%d)\r\n", val, val);
        } else {
            fprintf(stderr, "**CTRL %02x: 0x%X (%d)\r\n", ctrl, val, val);
        }
        return SCPI_RES_OK;
    }
    
    
    scpi_result_t SCPI_Reset(scpi_t * context) {
        (void) context;
    
    
        fprintf(stderr, "**Reset\r\n");
        return SCPI_RES_OK;
    }
    
    
    scpi_result_t SCPI_SystemCommTcpipControlQ(scpi_t * context) {
        (void) context;
    
    
        return SCPI_RES_ERR;
    }
    
    
    
    
    
    
    void error(char *msg)
    {
        perror(msg);
        exit(1);
    }
    
    
    int main(int argc, char *argv[])
    {
         int sockfd, portno, clilen;
         char buffer[256];
         struct sockaddr_in serv_addr, cli_addr;
         int n;
    
    
         SCPI_Init(&scpi_context,
                 scpi_commands,
                 &scpi_interface,
                 scpi_units_def,
                 SCPI_IDN1, SCPI_IDN2, SCPI_IDN3, SCPI_IDN4,
                 scpi_input_buffer, SCPI_INPUT_BUFFER_LENGTH,
                 scpi_error_queue_data, SCPI_ERROR_QUEUE_SIZE);
         if (argc < 2) {
             fprintf(stderr,"ERROR, no port provided\n");
             exit(1);
         }
         sockfd = socket(AF_INET, SOCK_STREAM, 0);
         if (sockfd < 0)
            error("ERROR opening socket");
         bzero((char *) &serv_addr, sizeof(serv_addr));
         portno = atoi(argv[1]);
         serv_addr.sin_family = AF_INET;
         serv_addr.sin_addr.s_addr = INADDR_ANY;
         serv_addr.sin_port = htons(portno);
         if (bind(sockfd, (struct sockaddr *) &serv_addr,
                  sizeof(serv_addr)) < 0)
                  error("ERROR on binding");
         listen(sockfd,5);
         clilen = sizeof(cli_addr);
         newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
         if (newsockfd < 0)
              error("ERROR on accept");
         while (1) {
             bzero(buffer,256);
             n = read(newsockfd,buffer,255);
             if (n < 0) {
            error("ERROR reading from socket");
             } else if (n > 0) {
                 printf("Here is the message: %s\n",buffer);
                 scpi_instrument_input(buffer, n);
    
    
             }
    
    
         }
    
    
         return 0;
    }

     

    My issue is in this small part:

     

         while (1) {
             bzero(buffer,256);
             n = read(newsockfd,buffer,255);
             if (n < 0) {
            error("ERROR reading from socket");
             } else if (n > 0) {
                 printf("Here is the message: %s\n",buffer);
                 scpi_instrument_input(buffer, n);
             }
         }

     

    As long as the client is connected, n = 0 when no data received from the client. All good

    When the client (example unix telnet session) closes, the read() function always returns the value 32.

    Before I define a fix for that, I want to try and understand why that is.

     

    Before anyone thinks that I'm a server side network socket expert: I borrowed this from http://www.cs.rpi.edu/~moorthy/Courses/os98/Pgms/server.c

    Because that program doesn't loop (it waits for traffic, notifies and dies), it doesn't have the issue.

     

    I need to do some more studying ...

    • Cancel
    • Vote Up +2 Vote Down
    • Reply
    • More
    • Cancel
  • shabaz
    shabaz over 4 years ago in reply to Jan Cumps

    Hi Jan,

     

    It's a while since I looked at the POSIX socket API, so I'm not sure either, but it might be worth also testing that newsockfd does not equal 0 before trying the read operation, e.g.

     

    if (newsockfd) {
      n=read(newsockfd, buffer, 255);
    }

     

    From memory I think using 'select' API is a good method to handle acting as a TCP server, but I need to read up on it. I'll try to dig up some useful links. There are some good patterns to handling socket reads, I just need to find them.

    • Cancel
    • Vote Up +2 Vote Down
    • Reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to shabaz

    You may be on to something. I only call newsockfd() once at the start and assign it to a global variable (blush). So it will never be 0, but it may very well be invalid.

     

    I'll check if I have to reallocate each time, or if I have to check that handle's validity ...

    • Cancel
    • Vote Up +1 Vote Down
    • Reply
    • More
    • Cancel
  • shabaz
    shabaz over 4 years ago in reply to Jan Cumps

    Hi Jan,

     

    This is using select, so not the same, but nevertheless they are handling the read slightly differently.

    The code tests for zero, and if it is zero, then the socket needs to be closed.

     

    Unfortunately that means you mean need a loop to open a new socket after that, i.e. to re-execute from your line 117 in your code I think.

    The photo is from 'Unix Network Programming' and it is old, so some things could have changed.

    • Cancel
    • Vote Up +2 Vote Down
    • Reply
    • More
    • Cancel
Comment
  • shabaz
    shabaz over 4 years ago in reply to Jan Cumps

    Hi Jan,

     

    This is using select, so not the same, but nevertheless they are handling the read slightly differently.

    The code tests for zero, and if it is zero, then the socket needs to be closed.

     

    Unfortunately that means you mean need a loop to open a new socket after that, i.e. to re-execute from your line 117 in your code I think.

    The photo is from 'Unix Network Programming' and it is old, so some things could have changed.

    • Cancel
    • Vote Up +2 Vote Down
    • Reply
    • More
    • Cancel
Children
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to shabaz

    I'm slowly getting there.

     

    This example keeps alive even if I close on the LabVIEW site. That's a step forward.

     

    #include<stdio.h>
    #include<string.h>    //strlen
    #include<sys/socket.h>
    #include<arpa/inet.h> //inet_addr
    #include<unistd.h>    //write
    
    
    int main(int argc , char *argv[])
    {
    int socket_desc , client_sock , c , read_size;
    struct sockaddr_in server , client;
    char client_message[2000];
    
    
    //Create socket
    socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    if (socket_desc == -1)
    {
    printf("Could not create socket");
    }
    puts("Socket created");
    
    
    //Prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons( 2222 );
    
    
    //Bind
    if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
    {
    //print the error message
    perror("bind failed. Error");
    return 1;
    }
    puts("bind done");
    while (1) {
    
    
    //Listen
    listen(socket_desc , 3);
    
    
    //Accept and incoming connection
    puts("Waiting for incoming connections...");
    c = sizeof(struct sockaddr_in);
    
    
    //accept connection from an incoming client
    client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
    if (client_sock < 0)
    {
    perror("accept failed");
    return 1;
    }
    puts("Connection accepted");
    
    
    //Receive a message from client
    while( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 )
    {
    //Send the message back to client
    write(client_sock , client_message , strlen(client_message));
    }
    
    
    if(read_size == 0)
    {
    puts("Client disconnected");
    fflush(stdout);
    }
    else if(read_size == -1)
    {
    perror("recv failed");
    }
    }
    
    
    return 0;
    }

    • Cancel
    • Vote Up 0 Vote Down
    • Reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to Jan Cumps

    I had a read on the timeout. It happens when the expected numbers of characters haven't arrived after the given timeout.

    Because TCP/IP in VISA doesn't support querying the number of characters waiting to be read (I've tried and I get Not Supported by this instrument), I have made this decision:

    After reading from the TCP/IP pipe, throw away that particular timeout error, because it is expected. Comment f you would do this differently.

    • Cancel
    • Vote Up 0 Vote Down
    • Reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2022 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • Facebook
  • Twitter
  • linkedin
  • YouTube