Post Go back to editing

Adalm Pluto Standalone Executable using OTG

Hello. I try to get an executable c program to run automatically at start from an USB stick using the OTG functionality of the Adalm Pluto. I called the file "runme0" without file extension, put in on a USB Stick and hoped that the fie will be executed automatically like shell scripts with the sh extension do without problem. I have tried to run the program directly via ssh and it ran with no problem. What shall I do to get this program to run? Is there a possibility to get an error log?

  • Can you post your shell script? How have you verified it's not working? Have you tried naming the script with the .sh extension?


  • I have tried the following: 

    1. Compiled the file on my windows machine. Result is the file runme0 here: (

    2. Connect the Pluto via USB to the PC and execute the file with SSH after copiing it via SCP. This worked fine.

    3. Copy the executable file to an USB stick and using the OTG functionality according to In the standalone mode the pluto did not execute the file.

    4. Copy an additional shell script to the USB stick called "":

    cd /media/sda1/
    chmod +x runme0

    5. The script has been executed in standalone mode but the executable file "runme0" did not start. At least the shell script has been executed as I have added some additional commands but the pluto did not start the compiled file "runme0".

    Does anyone know what went wrong? Is there a possibility to log the command line error Messages into a file on the USB stick as this would help a lot for Debugging?



  • Please do not name the executable runme0, as the autorun script might get confused. Naming the shell script is fine.

    I would not directly use that example since it has no end or stop point.


  • I have tried to rename the executable but it still did not work. Is there a reason why the program does not run directly via OTG? Do you have another example that proves the concept of executing programs via OTG?  

  • I changed the c example to run for only a few iterations of the loop and it worked fine.

    Here is the script:

    # the default directory the script runs in is /dev, so change to the drive
    cd /media/sda1/
    # create a file
    touch foobar.txt
    echo default-on > /sys/class/leds/led0:green/trigger >> foobar.txt
    sleep 3
    chmod +x pluto_stream
    ./pluto_stream >> foobar.txt
    echo none > /sys/class/leds/led0:green/trigger >> foobar.txt
    # Set the LO up
    /usr/bin/iio_attr -a -c ad9361-phy TX_LO frequency 908460000 >> foobar.txt
    # Set the Sample frequency up, tone will appear at sampling_frequency/32
    /usr/bin/iio_attr -a -c -o ad9361-phy voltage0 sampling_frequency 32000000 >> foobar.txt
    # Turn the attenuation down
    /usr/bin/iio_attr -a -c -o ad9361-phy voltage0 hardwaregain 0
    # Inject 0dBFS tone at Fsample/32 into TX (all channels enabled)
    /usr/bin/iio_attr -a -D ad9361-phy bist_tone "1 0 0 0" >> foobar.txt
    cd /root
    ACTION=remove_all /lib/mdev/

    Here is the modified c example:

     * libiio - AD9361 IIO streaming example
     * Copyright (C) 2014 IABG mbH
     * Author: Michael Feilen <>
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * as published by the Free Software Foundation; either version 2
     * of the License, or (at your option) any later version.
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * GNU General Public License for more details.
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
    #include <stdbool.h>
    #include <stdint.h>
    #include <string.h>
    #include <signal.h>
    #include <stdio.h>
    #ifdef __APPLE__
    #include <iio/iio.h>
    #include <iio.h>
    /* helper macros */
    #define MHZ(x) ((long long)(x*1000000.0 + .5))
    #define GHZ(x) ((long long)(x*1000000000.0 + .5))
    #define IIO_ENSURE(expr) { \
    	if (!(expr)) { \
    		(void) fprintf(stderr, "assertion failed (%s:%d)\n", __FILE__, __LINE__); \
    		(void) abort(); \
    	} \
    /* RX is input, TX is output */
    enum iodev { RX, TX };
    /* common RX and TX streaming params */
    struct stream_cfg {
    	long long bw_hz; // Analog banwidth in Hz
    	long long fs_hz; // Baseband sample rate in Hz
    	long long lo_hz; // Local oscillator frequency in Hz
    	const char* rfport; // Port name
    /* static scratch mem for strings */
    static char tmpstr[64];
    /* IIO structs required for streaming */
    static struct iio_context *ctx   = NULL;
    static struct iio_channel *rx0_i = NULL;
    static struct iio_channel *rx0_q = NULL;
    static struct iio_channel *tx0_i = NULL;
    static struct iio_channel *tx0_q = NULL;
    static struct iio_buffer  *rxbuf = NULL;
    static struct iio_buffer  *txbuf = NULL;
    static bool stop;
    /* cleanup and exit */
    static void shutdown()
    	printf("* Destroying buffers\n");
    	if (rxbuf) { iio_buffer_destroy(rxbuf); }
    	if (txbuf) { iio_buffer_destroy(txbuf); }
    	printf("* Disabling streaming channels\n");
    	if (rx0_i) { iio_channel_disable(rx0_i); }
    	if (rx0_q) { iio_channel_disable(rx0_q); }
    	if (tx0_i) { iio_channel_disable(tx0_i); }
    	if (tx0_q) { iio_channel_disable(tx0_q); }
    	printf("* Destroying context\n");
    	if (ctx) { iio_context_destroy(ctx); }
    static void handle_sig(int sig)
    	printf("Waiting for process to finish... Got signal %d\n", sig);
    	stop = true;
    /* check return value of attr_write function */
    static void errchk(int v, const char* what) {
    	 if (v < 0) { fprintf(stderr, "Error %d writing to channel \"%s\"\nvalue may not be supported.\n", v, what); shutdown(); }
    /* write attribute: long long int */
    static void wr_ch_lli(struct iio_channel *chn, const char* what, long long val)
    	errchk(iio_channel_attr_write_longlong(chn, what, val), what);
    /* write attribute: string */
    static void wr_ch_str(struct iio_channel *chn, const char* what, const char* str)
    	errchk(iio_channel_attr_write(chn, what, str), what);
    /* helper function generating channel names */
    static char* get_ch_name(const char* type, int id)
    	snprintf(tmpstr, sizeof(tmpstr), "%s%d", type, id);
    	return tmpstr;
    /* returns ad9361 phy device */
    static struct iio_device* get_ad9361_phy(struct iio_context *ctx)
    	struct iio_device *dev =  iio_context_find_device(ctx, "ad9361-phy");
    	IIO_ENSURE(dev && "No ad9361-phy found");
    	return dev;
    /* finds AD9361 streaming IIO devices */
    static bool get_ad9361_stream_dev(struct iio_context *ctx, enum iodev d, struct iio_device **dev)
    	switch (d) {
    	case TX: *dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc"); return *dev != NULL;
    	case RX: *dev = iio_context_find_device(ctx, "cf-ad9361-lpc");  return *dev != NULL;
    	default: IIO_ENSURE(0); return false;
    /* finds AD9361 streaming IIO channels */
    static bool get_ad9361_stream_ch(__notused struct iio_context *ctx, enum iodev d, struct iio_device *dev, int chid, struct iio_channel **chn)
    	*chn = iio_device_find_channel(dev, get_ch_name("voltage", chid), d == TX);
    	if (!*chn)
    		*chn = iio_device_find_channel(dev, get_ch_name("altvoltage", chid), d == TX);
    	return *chn != NULL;
    /* finds AD9361 phy IIO configuration channel with id chid */
    static bool get_phy_chan(struct iio_context *ctx, enum iodev d, int chid, struct iio_channel **chn)
    	switch (d) {
    	case RX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("voltage", chid), false); return *chn != NULL;
    	case TX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("voltage", chid), true);  return *chn != NULL;
    	default: IIO_ENSURE(0); return false;
    /* finds AD9361 local oscillator IIO configuration channels */
    static bool get_lo_chan(struct iio_context *ctx, enum iodev d, struct iio_channel **chn)
    	switch (d) {
    	 // LO chan is always output, i.e. true
    	case RX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("altvoltage", 0), true); return *chn != NULL;
    	case TX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("altvoltage", 1), true); return *chn != NULL;
    	default: IIO_ENSURE(0); return false;
    /* applies streaming configuration through IIO */
    bool cfg_ad9361_streaming_ch(struct iio_context *ctx, struct stream_cfg *cfg, enum iodev type, int chid)
    	struct iio_channel *chn = NULL;
    	// Configure phy and lo channels
    	printf("* Acquiring AD9361 phy channel %d\n", chid);
    	if (!get_phy_chan(ctx, type, chid, &chn)) {	return false; }
    	wr_ch_str(chn, "rf_port_select",     cfg->rfport);
    	wr_ch_lli(chn, "rf_bandwidth",       cfg->bw_hz);
    	wr_ch_lli(chn, "sampling_frequency", cfg->fs_hz);
    	// Configure LO channel
    	printf("* Acquiring AD9361 %s lo channel\n", type == TX ? "TX" : "RX");
    	if (!get_lo_chan(ctx, type, &chn)) { return false; }
    	wr_ch_lli(chn, "frequency", cfg->lo_hz);
    	return true;
    /* simple configuration and streaming */
    /* usage: 
     * Default context, assuming local IIO devices, i.e., this script is run on ADALM-Pluto for example
     * URI context, find out the uri by typing `iio_info -s` at the command line of the host PC
     $./a.out usb:x.x.x 
    int main (int argc, char **argv)
    	// Streaming devices
    	struct iio_device *tx;
    	struct iio_device *rx;
    	// RX and TX sample counters
    	size_t nrx = 0;
    	size_t ntx = 0;
    	// Stream configurations
    	struct stream_cfg rxcfg;
    	struct stream_cfg txcfg;
    	// Listen to ctrl+c and IIO_ENSURE
    	signal(SIGINT, handle_sig);
    	// RX stream config
    	rxcfg.bw_hz = MHZ(2);   // 2 MHz rf bandwidth
    	rxcfg.fs_hz = MHZ(2.5);   // 2.5 MS/s rx sample rate
    	rxcfg.lo_hz = GHZ(2.5); // 2.5 GHz rf frequency
    	rxcfg.rfport = "A_BALANCED"; // port A (select for rf freq.)
    	// TX stream config
    	txcfg.bw_hz = MHZ(1.5); // 1.5 MHz rf bandwidth
    	txcfg.fs_hz = MHZ(2.5);   // 2.5 MS/s tx sample rate
    	txcfg.lo_hz = GHZ(2.5); // 2.5 GHz rf frequency
    	txcfg.rfport = "A"; // port A (select for rf freq.)
    	printf("* Acquiring IIO context\n");
    	if (argc == 1) {
    		IIO_ENSURE((ctx = iio_create_default_context()) && "No context");
    	else if (argc == 2) {
    		IIO_ENSURE((ctx = iio_create_context_from_uri(argv[1])) && "No context");
    	IIO_ENSURE(iio_context_get_devices_count(ctx) > 0 && "No devices");
    	printf("* Acquiring AD9361 streaming devices\n");
    	IIO_ENSURE(get_ad9361_stream_dev(ctx, TX, &tx) && "No tx dev found");
    	IIO_ENSURE(get_ad9361_stream_dev(ctx, RX, &rx) && "No rx dev found");
    	printf("* Configuring AD9361 for streaming\n");
    	IIO_ENSURE(cfg_ad9361_streaming_ch(ctx, &rxcfg, RX, 0) && "RX port 0 not found");
    	IIO_ENSURE(cfg_ad9361_streaming_ch(ctx, &txcfg, TX, 0) && "TX port 0 not found");
    	printf("* Initializing AD9361 IIO streaming channels\n");
    	IIO_ENSURE(get_ad9361_stream_ch(ctx, RX, rx, 0, &rx0_i) && "RX chan i not found");
    	IIO_ENSURE(get_ad9361_stream_ch(ctx, RX, rx, 1, &rx0_q) && "RX chan q not found");
    	IIO_ENSURE(get_ad9361_stream_ch(ctx, TX, tx, 0, &tx0_i) && "TX chan i not found");
    	IIO_ENSURE(get_ad9361_stream_ch(ctx, TX, tx, 1, &tx0_q) && "TX chan q not found");
    	printf("* Enabling IIO streaming channels\n");
    	printf("* Creating non-cyclic IIO buffers with 1 MiS\n");
    	rxbuf = iio_device_create_buffer(rx, 1024*1024, false);
    	if (!rxbuf) {
    		perror("Could not create RX buffer");
    	txbuf = iio_device_create_buffer(tx, 1024*1024, false);
    	if (!txbuf) {
    		perror("Could not create TX buffer");
    	printf("* Starting IO streaming (press CTRL+C to cancel)\n");
    	int count = 0;
    	while (!stop)
    		if (count>=20)
    		ssize_t nbytes_rx, nbytes_tx;
    		char *p_dat, *p_end;
    		ptrdiff_t p_inc;
    		// Schedule TX buffer
    		nbytes_tx = iio_buffer_push(txbuf);
    		if (nbytes_tx < 0) { printf("Error pushing buf %d\n", (int) nbytes_tx); shutdown(); }
    		// Refill RX buffer
    		nbytes_rx = iio_buffer_refill(rxbuf);
    		if (nbytes_rx < 0) { printf("Error refilling buf %d\n",(int) nbytes_rx); shutdown(); }
    		// READ: Get pointers to RX buf and read IQ from RX buf port 0
    		p_inc = iio_buffer_step(rxbuf);
    		p_end = iio_buffer_end(rxbuf);
    		for (p_dat = (char *)iio_buffer_first(rxbuf, rx0_i); p_dat < p_end; p_dat += p_inc) {
    			// Example: swap I and Q
    			const int16_t i = ((int16_t*)p_dat)[0]; // Real (I)
    			const int16_t q = ((int16_t*)p_dat)[1]; // Imag (Q)
    			((int16_t*)p_dat)[0] = q;
    			((int16_t*)p_dat)[1] = i;
    		// WRITE: Get pointers to TX buf and write IQ to TX buf port 0
    		p_inc = iio_buffer_step(txbuf);
    		p_end = iio_buffer_end(txbuf);
    		for (p_dat = (char *)iio_buffer_first(txbuf, tx0_i); p_dat < p_end; p_dat += p_inc) {
    			// Example: fill with zeros
    			// 12-bit sample needs to be MSB alligned so shift by 4
    			((int16_t*)p_dat)[0] = 0 << 4; // Real (I)
    			((int16_t*)p_dat)[1] = 0 << 4; // Imag (Q)
    		// Sample counter increment and status output
    		nrx += nbytes_rx / iio_device_get_sample_size(rx);
    		ntx += nbytes_tx / iio_device_get_sample_size(tx);
    		printf("\tRX %8.2f MSmp, TX %8.2f MSmp\n", nrx/1e6, ntx/1e6);
    	return 0;

    Here is the output I get in OTG:

    Using auto-detected IIO context at URI "local:"
    dev 'ad9361-phy', channel 'altvoltage1' (output), id 'TX_LO', attr 'frequency', value '2450000000'
    wrote 10 bytes to frequency
    dev 'ad9361-phy', channel 'altvoltage1' (output), id 'TX_LO', attr 'frequency', value '908460000'
    Using auto-detected IIO context at URI "local:"
    dev 'ad9361-phy', channel 'voltage0' (output), attr 'sampling_frequency', value '30720000'
    wrote 9 bytes to sampling_frequency
    dev 'ad9361-phy', channel 'voltage0' (output), attr 'sampling_frequency', value '32000000'
    Using auto-detected IIO context at URI "local:"
    dev 'ad9361-phy', debug attr 'bist_tone', value :'0'
    wrote 8 bytes to bist_tone
    dev 'ad9361-phy', debug attr 'bist_tone', value :'1'
    * Acquiring IIO context
    * Acquiring AD9361 streaming devices
    * Configuring AD9361 for streaming
    * Acquiring AD9361 phy channel 0
    * Acquiring AD9361 RX lo channel
    * Acquiring AD9361 phy channel 0
    * Acquiring AD9361 TX lo channel
    * Initializing AD9361 IIO streaming channels
    * Enabling IIO streaming channels
    * Creating non-cyclic IIO buffers with 1 MiS
    * Starting IO streaming (press CTRL+C to cancel)
    	RX     1.05 MSmp, TX     1.05 MSmp
    	RX     2.10 MSmp, TX     2.10 MSmp
    	RX     3.15 MSmp, TX     3.15 MSmp
    	RX     4.19 MSmp, TX     4.19 MSmp
    	RX     5.24 MSmp, TX     5.24 MSmp
    	RX     6.29 MSmp, TX     6.29 MSmp
    	RX     7.34 MSmp, TX     7.34 MSmp
    	RX     8.39 MSmp, TX     8.39 MSmp
    	RX     9.44 MSmp, TX     9.44 MSmp
    	RX    10.49 MSmp, TX    10.49 MSmp
    	RX    11.53 MSmp, TX    11.53 MSmp
    	RX    12.58 MSmp, TX    12.58 MSmp
    	RX    13.63 MSmp, TX    13.63 MSmp
    	RX    14.68 MSmp, TX    14.68 MSmp
    	RX    15.73 MSmp, TX    15.73 MSmp
    	RX    16.78 MSmp, TX    16.78 MSmp
    	RX    17.83 MSmp, TX    17.83 MSmp
    	RX    18.87 MSmp, TX    18.87 MSmp
    	RX    19.92 MSmp, TX    19.92 MSmp
    * Destroying buffers
    * Disabling streaming channels
    * Destroying context
    Using auto-detected IIO context at URI "local:"
    dev 'ad9361-phy', channel 'altvoltage1' (output), id 'TX_LO', attr 'frequency', value '2500000000'
    wrote 10 bytes to frequency
    dev 'ad9361-phy', channel 'altvoltage1' (output), id 'TX_LO', attr 'frequency', value '908460000'
    Using auto-detected IIO context at URI "local:"
    dev 'ad9361-phy', channel 'voltage0' (output), attr 'sampling_frequency', value '2500000'
    wrote 9 bytes to sampling_frequency
    dev 'ad9361-phy', channel 'voltage0' (output), attr 'sampling_frequency', value '32000000'
    Using auto-detected IIO context at URI "local:"
    dev 'ad9361-phy', debug attr 'bist_tone', value :'0'
    wrote 8 bytes to bist_tone
    dev 'ad9361-phy', debug attr 'bist_tone', value :'1'


  • Your example stil does not work. The test tone commands in your file lead to a transmit signal but before the "pluto_stream" program should start. This is not the case. On the other Hand, where do you get the OTG Output? Do you find it on the USB drive after execution? I can not find any file. I will describe now exactly the steps I do so you may see, where the Problem lies:

    1. The preparation of the data is done on my Windows10 machine.

    2. I copy the content of your "" example to a Textfile with the Name "" and write it to a clean 2 GB USB Drive with FAT32 file Format.

    3. I copy the Content of your c example Code above in a Textfile called "pluto_stream.c".

    4. Using the Compiler "gcc-arm-9.2-2019.12-mingw-w64-i686-arm-none-linux-gnueabihf" and the Sysroot v0.31 (same Version as in the Pluto) I perform the compilation from the Folder the c file is put to with following command: 

    arm-none-linux-gnueabihf-gcc -mfloat-abi=hard  --sysroot=C:\Users\hanno_000\Documents\Amateurfunk\Pluto\sysroot-v0.31.tar\staging -std=gnu99 -g -o pluto_stream pluto_stream.c -lpthread -liio -lm -Wall -Wextra

    5. I copy the Output file "pluto_stream" to the USB drive.

    6. The Pluto is supplied via the Right Mini USB Input.

    7. I insert the USB drive to the Pluto.

    8. The blinking frequency changes for a short period then goes back to the normal Flash interval.

    9. Check if signals are on the Air.

    10. Remove USB drive, put it to the PC, there is no additional log file on it.

    I have to say that the reason why I believe that the executable file is not started is because I made an Experiment by commenting out the test tone and modified your c source Code just to get a sine Signal at 144.9MHz to see if it works. The program itself runs as expected directly via ssh but still not standalone. When I then activate your test tone commands in the "" script (changing the transmit frequency to 144.9MHz) the signal is sent.

    So still the same Problem, the script runs but the c program not. Do you have an idea?

  • So if no log file is created then the script isn't running, the USB isn't writable, or the script has some encoding which Pluto cannot interpret.

    Here is my generated executable (rename to pluto_stream)


Reply Children
  • Hello Travis,

    I tried your executable after renaming it correctly. The program still does not execute and the file is not generated. The rest of the script is running so I am now Looking towards the write Access of the USB stick. This may explain why the executable is not running because the "chmod" command could be rejected and the executable therefore is not startable. 

    I tried with 2 USB Sticks now but no success. Did you use FAT or another filesystem? Do you format your drive with Linux or windows? Maybe there are differences.

  • sdf                                                                
    └─sdf1        vfat   TEST     4017-B37F                            /media/tcollins/TEST
    └─sdg1        vfat   PlutoSDR F6F3-FAA0                            /media/tcollins/PlutoSDR

    Everything is done in Linux from my side.


  • Hi Travis,

    I have tried now to check if the USB drive is writable under Linux using my RaspberryPi4. After plugging the USB drive in a free Slot the USB drive was automatically mounted as expected and read and write Access was granted.

    Then I unmounted the Drive manually and remount it again with the procedure in the script on the pluto:

    1. mkdir -p "/media/sda1"

    2. mount -t auto -o sync "/dev/sda1" "/media/sda1"

    After this Manual mounting I had no permission to write to the USB drive anymore. So I think the same happens on the Pluto. I do not know what the difference is between the automounter on the raspian and the manual mounting but at this Point there seems to be the Problem. Also after trying to Change the Access rights with "chown" or "chmod" did not succeed in the Manual mounting mode. Maybe you have an idea.

  • I would suggest formatting on a Linux system if you can. Maybe Windows is doing something strange.


  • The executable works now and also the log file is generated. It had Nothing to do with the USB drive, it was the script file that was generated with on Windows. I opened the file with a Linux System, copied the content to a new text file generated in Linux. That was it. There has been a hint on the OTG page around here saying to insert 'LF' at the end of the line using Windows, I think it had something to do with it.