Loading tmux on Boot in Linux

ss-tmux2

tmux is a wonderful tool for displaying virtual consoles on the linux command prompt screen. It’s the next best thing to getting actual GUI windows controllable with a mouse.

Mainly, I use it for ssh purposes. Where I can ssh to a pc that I know has tmux already launched in the background and type.

tmux a

which attaches the session to the on-going tmux background session, allowing you to see everything that is going on in that process. This is especially useful for embedded systems where there are multiple processes launched in the background and you want to monitor them all.

So I have a tmux script launcher.sh here:

#!/bin/bash

SESSION="MPC1"

#allow re-launch
/usr/bin/tmux has-session -t $SESSION 2> /dev/null && /usr/bin/tmux kill-session -t $SESSION
/usr/bin/tmux -2 new-session -d -s $SESSION

echo "Launching tmux"

/usr/bin/tmux split-window -h
/usr/bin/tmux split-window -v
/usr/bin/tmux select-pane -t 0

/usr/bin/tmux send-keys -t $SESSION.0 "cd /path/to/binary1folder" C-m
/usr/bin/tmux send-keys -t $SESSION.0 "./binary1" C-m

/usr/bin/tmux send-keys -t $SESSION.1 "cd /path/to/binary2folder" C-m
/usr/bin/tmux send-keys -t $SESSION.1 "./binary2" C-m

/usr/bin/tmux send-keys -t $SESSION.2 "cd /path/to/binary3folder" C-m
/usr/bin/tmux send-keys -t $SESSION.2 "./binary3" C-m

This basically opens up , three panes and splits the window horizontally first, then splitting again one of the split windows vertically. It then launches a binary in each of the window panes. I won’ t go too much into the scripting here as there are plenty of resources for doing so, like this.

Configuring tmux to boot on startup on CentOS 7

Normally this should be pretty straightforward, but I ran into some hiccups.

First,

sudo nano /etc/rc.local

And edit the rc.local file to include

su -c /path/toyourscript/launcher.sh -l your_user_id

-l your_user_id means that you do the launch of the script launcher.sh as the user your_user_id.

Make sure your rc.local is executable.

sudo chmod +x /etc/rc.local

And by right it should launch when CentOS boots, launching launcher.sh in the background which in turn launches tmux. However, I found that one of the abrt startup scripts, abrt-console-notification.sh was interfering with the launching of the tmux process/binaries. It would hang at the console terminal of the tmux screens. Doing the following resolved the problem for me.

cd /etc/profile.d/
chmod -r abrt-console-notification.sh

Basically, make abrt-console-notification.sh non-readable, allowing the profile.d startup process to skip over this particular script. It’s kind of a hack, but it worked. I reckon at most I don’t get the automatic bug reporting tool notifications at the console. Note that this script is run depending on what type of installation you chose when installing CentOS. I think that the minimal installl doesn’t run into this issue.

Hope this it useful to you, let me know!

ps. Here’s a tmux cheat sheet. https://gist.githubusercontent.com/afair/3489752/raw/e7106ac93c8f9602d3843696692a87cfb43c2d21/tmux.cheat

Texas Instruments C6678 Evaluation Kit – Booting Your Program from NAND/NOR

5280-cimg3088-_2d00_-small

If you’ve bought a evaluation kit for the Texas Instruments C6678 evaluation kit, and am wondering how to get your .out program binary to load on boot, and am confused by the manuals, here are a set of “golden path” instructions. (Actually, most of it is cut-and-pasted from various readme.txt files in different directories. It’s just placed here in proper order for normal people like me to understand.)

Procedure to Modify I2C Configuration

For some reason, the evaluation kit ships with the EEPROM’s nandBoot.bootFormat setting at ibl_BOOT_FORMAT_BBLOB. If we are using the .out file created with Code Composer, we need to change this is ibl_BOOT_FORMAT_ELF in order to boot the binary.

You likely only need to do this once to change the I2C configuration. Fortunately, TI has a helper program that makes use of the file i2cConfig.gel to change the I2C program.

- Modify the i2cConfig.gel file to your needs. It is located at C:\ti\mcsdk_2_01_02_05\tools\boot_loader\ibl\src\util\i2cConfig\i2cConfig.gel
 - Look for: setConfig_c6678_main() , this is the function you will calll later to set the i2c config
 - Look for: ibl.bootModes[1].u.nandBoot.bootFormat : If this is ibl_BOOT_FORMAT_BBLOB, change to ibl_BOOT_FORMAT_ELF;

- Load NO BOOT as usual , Launch Selected Configuration
 Set the dip switches (pin1, pin2, pin3, pin4) to:
 SW3(off, on, on, on),
 SW4(on, on, on, on),
 SW5(on, on, on, on),
 SW6(on, on, on, on)
 - Connect Target on Core 0
 - Load C:\ti\mcsdk_2_01_02_05\tools\boot_loader\ibl\src\make\bin\i2cparam_0x51_c6678_le_0x500.out program
 - Gel Files -> Load Gel File C:\ti\mcsdk_2_01_02_05\tools\boot_loader\ibl\src\util\i2cConfig\i2cConfig.gel
 - Scripts -> evm C6678 IBL -> setConfig_c6678_main()
 - Done

Procedure to Load NOR

NOR Writer Utility

NOR Writer is a simple utility to program a CCS format image/data file to the NOR flash.

Steps to program the NOR:

1. Be sure to set the boot mode dip switch to no boot/EMIF16 boot mode on the EVM.

2. Copy the binary file to writer\nor\evmc66xxl\bin directory, and rename it to app.bin.

3. Change the file_name and start_addr in writer\nor\evmc66xxl\bin\norwriter_input.txt if necessary.
 By default the NOR writer will load app.bin to DSP memory and write the data to NOR device start byte address 0,
 the start_addr should always be set to the start byte addess of a sector.

4. Open CCSv5 and launch the evmc66xx emulator target configuration and connect to core 0.

5. Load the program writer\nor\evmc66xxl\bin\norwriter_evm66xxl.out to CCS, be sure evmc66xxl.gel is used in CCS
 and DDR is intialized.

6. Open the Memory view (in CCSv5, view->Memory Browser), and view the memory address 0x80000000.

7. Load app.bin to 0x80000000:
 * In CCSv5, right click mouse in memory window, select "load memory".
 * Browse and select writer\nor\evmc66xxl\bin\app.bin (raw data format), click "next"
 * Set the Start Address to "0x80000000", Type-size to 32-bits, leave swap unchecked, click "finish"
 8. After the binary file is loaded into the memory, run the program (in CCSv5, press F8), it will start to program the
 NOR.

9. When programming is completed, the console will print "NOR programming completed successfully", if there
 is any error, the console will show the error message.

NOR Boot:
 Set the dip switches (pin1, pin2, pin3, pin4) to:
 SW3(off, off, on, off),
 SW4(on, on, on, on),
 SW5(on, on, on, off),
 SW6(on, on, on, on)
 This will set the boot param index to 0 to boot the NOR image, by default
 the boot configuration table sets the NOR offset address to be 0 and
 image format to be ELF for image 0.

 Procedure to Load NAND

– Programming the application on NAND or NOR flash
NOTE: This step is not needed if the application is booted from Ethernet.
(a) Use the NAND or NOR writer c6678 EVM from the tools directory.
(a) Flash the Application to NAND or NOR. For instructions please follow
the instructions given along with the NAND/NOR writer.

For all the I2C boot modes, user needs to set the boot dip switches to I2C master, bus address 0x51.
NAND Writer Utility

NAND Writer is a simple utility to program a CCS format image/data file to the NAND flash.

Steps to program the NAND:

1. Be sure to set the boot mode dip switch to no boot/EMIF16 boot mode on the EVM.

2. Copy the binary file to writer\nand\evmc66xxl\bin directory, and rename it to app.bin.

3. Change the file_name and start_addr in writer\nand\evmc66xxl\bin\nandwriter_input.txt if necessary.
 By default the NAND writer will load app.bin to DSP memory and write the data to NAND device start byte address 16384
 (start address of block 1). The start_addr should always be set to the start byte addess of a block.

4. Open CCSv5 and launch the evmc66xx emulator target configuration and connect to core 0.

5. Load the program writer\nand\evmc66xxl\bin\nandwriter_evm66xxl.out to CCS, be sure evmc66xxl.gel is used in CCS
 and DDR is intialized.

6. Open the Memory view (in CCSv5, view->Memory Browser), and view the memory address 0x80000000.

7. Load app.bin to 0x80000000:
 * In CCSv5, right click mouse in memory window, select "load memory".
 * Browse and select writer\nand\evmc66xxl\bin\app.bin (raw data format), click "next"
 * Set the Start Address to "0x80000000", Type-size to 32-bits, leave swap unchecked, click "finish"

8. After the data file is loaded into the memory, run the program (in CCSv5, press F8), it will start to program the
 NAND.

9. When programming is completed, the console will print "NAND programming completed successfully", if there
 is any error, the console will show the error message.

NAND Boot:
 Set the dip switches (pin1, pin2, pin3, pin4) to:
 SW3(off, off, on, off),
 SW4(on, off, on, on),
 SW5(on, on, on, off),
 SW6(on, on, on, on)
 This will set the boot param index to 2 to boot the NAND image, by default
 the boot configuration table sets the NAND offset address to be 16384
 (start of block 1).

Valgrind – Dealing with IPP / AVX Related False Positives

Update: Valgrind 3.11 doesn’t show the AVX errors mentioned below. So if you have the option, upgrading it to 3.11 is probably the better option. 

Debugging Intel IPP-enabled C/C++ programs with Valgrind, you may run into the following issues.

Process terminating with default action of signal 4 (SIGILL)

Illegal opcode at address 0xEBC9CD4

at 0xEBC9CD4 : own_ipps_sAtan2_E9LAynn (in /opt/intel/compilers_and_libraries_2016.0.109/linux/ipp/lib/intel64_lin/libippvme9.so.9.0

The program terminates because of this apparently “illegal opcode” that valgrind doesn’t recognize.

This is ok. It’s just that Valgrind doesn’t recognize certain AVX opcodes.
If you want Valgrind to proceed anyway, do this:

From the IPP Manual, you can find this:

http://www.hpc.ut.ee/dokumendid/ips_xe_2015/composerxe/Documentation/en_US/ipp/ipp_manual/GUID-C730D3B1-6232-45AF-A757-DF52850388CD.htm

32-bit code:

#define PX_FM ( ippCPUID_MMX | ippCPUID_SSE )
#define W7_FM ( PX_FM | ippCPUID_SSE2 )
#define V8_FM ( W7_FM | ippCPUID_SSE3 | ippCPUID_SSSE3 )
#define S8_FM ( V8_FM | ippCPUID_MOVBE )
#define P8_FM ( V8_FM | ippCPUID_SSE41 | ippCPUID_SSE42 | ippCPUID_AES | ippCPUID_CLMUL | ippCPUID_SHA )
#define G9_FM ( P8_FM | ippCPUID_AVX | ippAVX_ENABLEDBYOS | ippCPUID_RDRRAND | ippCPUID_F16C )
#define H9_FM ( G9_FM | ippCPUID_AVX2 | ippCPUID_MOVBE | ippCPUID_ADCOX | ippCPUID_RDSEED | ippCPUID_PREFETCHW )
64-bit code:

#define PX_FM ( ippCPUID_MMX | ippCPUID_SSE | ippCPUID_SSE2 )
#define M7_FM ( PX_FM | ippCPUID_SSE3 )
#define N8_FM ( S8_FM )
#define U8_FM ( V8_FM )
#define Y8_FM ( P8_FM )
#define E9_FM ( G9_FM )
#define L9_FM ( H9_FM )

Copy and paste these on the top of your code. Just until P8_FM for this case will do. So you can actually “use” P8_FM, which essential means that ipp will use the SSE type instructions and will avoid the AVX types.

#define PX_FM ( ippCPUID_MMX | ippCPUID_SSE )
#define W7_FM ( PX_FM | ippCPUID_SSE2 )
#define V8_FM ( W7_FM | ippCPUID_SSE3 | ippCPUID_SSSE3 )
#define S8_FM ( V8_FM | ippCPUID_MOVBE )
#define P8_FM ( V8_FM | ippCPUID_SSE41 | ippCPUID_SSE42 | ippCPUID_AES | ippCPUID_CLMUL | ippCPUID_SHA )

// then in your main()
ippInit();
 
ippSetCpuFeatures(P8_FM) // -- purely to deal with valgrind false positives. Comment out if you want maximum performance using AVX.

I’m using the Valgrind that comes with CentOS 7, 3.10.0. Do let me know if this has been fixed in 3.11. 🙂

How to connect to WPA/WPA2 Wifi Network in CM-FX6 (Debian)

The default Wifi device name is mlan0 on the iMX6. We will bring this wifi link up.

> ip link show mlan0

> ip link set mlan0 up

Then check whether mlan0 is up.

> ip link show mlan0

You should see there is a UP in the text that you see.

To scan for wifi networks.

> iwlist scan

Keep a look out for the SSID name, this is the Wifi network name you want to connect to.

Create a passphrase to connect to the Wifi SSID.

> wpa_passphrase <wifiname> >> /etc/wpa_supplicant.conf

(type in the passphrase and press enter)

> cat /etc/wpa_supplicant.conf

You should see your passphrase in the conf file.

Run wpa_supplicant with the new conf file.

> wpa_supplicant -B -D wext -i mlan0 -c /etc/wpa_supplicant.conf

-B means run wpa_supplicant in the background.
-D specifies the wireless driver. wext is the generic driver.
-c specifies the path for the configuration file.

Obtain the IP address by DHCP.

> dhclient mlan0

> ip route show

You should see that mlan0 is an entry in the route table.

At this juncture, you are connected to the internet. Congratulations!

TCP/IP over Serial Line using pppd (Linux)

Using TCP/IP allows data to be sent as packets, instead of a byte stream as per serial. The advantage is that this prevents mis-interpreting messages sent, when bytes are dropped, or the definition of the message is different.

Suppose we have:

Computer1 -> USB-Serial Converter -> Serial Cable -> Serial->USB Converter ->  Computer 2

On computer 1 we type :

pppd /dev/ttyUSB0 115200 lock nodetach noauth debug 195.0.0.12:195.0.0.13

On computer 2 we type:

pppd /dev/ttyUSB0 115200 lock nodetach noauth debug

And watch it connect!

You should see a new device (usually ppp0) when you type:

ifconfig

See full pppd documentation here: http://linux.die.net/man/8/pppd

Increasing Connection Re-tries

Should you need the pppd to keep trying to connect, you can add the following flag at the back of your command – lcp-max-configure

So,

pppd /dev/ttyUSB0 115200 lock nodetach noauth debug lcp-max-configure 1000000 195.0.0.12:195.0.0.13

where 1000000 is number of times pppd tries to connect to the other party.

C – sleep()ing Correctly …

Recently I needed to do a write to a file every 1/25th of a second. I realised that there is no guarantees on a non-real time system (Debian in this case) that the code is executed exactly once every 0.04 seconds. This is how I did it.

Enter clockGetTime() -> this has precision in the nanoseconds in the time.h library. Be sure to include real time flag -lrt in your project’s linker.

actualSleep is the amount of sleep actually needed when you factor in the amount of time used to execute other statements like the print statement.

In this way, you only sleep() the amount of time you really need to sleep(), and not more. If sleep() were a constant value 1/25 , the amount of delay in between sleeps would be more than expected (by a little, but it adds up). If you do the measurements you would see your results skewing in terms of real time. I noticed this in the first place as my statement was logging GPS UTC time to a file.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <limits.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <wait.h>
#include <ctype.h>
#include <time.h>
#include <string.h>
#include <sys/statvfs.h>

int getUsecElapsedFromStart(const struct timespec* tstart) {
    
    struct timespec tnow; 
    clock_gettime(CLOCK_MONOTONIC, &tnow); 
    
    return (int)((tnow.tv_sec*10.0e9 + tnow.tv_nsec) - (tstart->tv_sec*10.0e9 + tstart->tv_nsec)); 
}

int getTimeSpecDiff(struct timespec start, struct timespec end) {
    struct timespec temp; 
    
    if (((end.tv_nsec-start.tv_nsec))<0) {
        temp.tv_sec = end.tv_sec - start.tv_sec - 1;
        temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
    }
    else {
        temp.tv_sec = end.tv_sec - start.tv_sec;
        temp.tv_nsec = end.tv_nsec - start.tv_nsec; 
    }
    // -- convert difference to micro
    return (int)(temp.tv_sec * 10.0e6 + temp.tv_nsec * 10.0e-3);
}
/*
 * 
 */
int main(int argc, char** argv) {

    long  actualSleep = 0;
    long usecToSleep = 0;
    long usecElapsed = 0; 
    struct timespec tstart;
    struct timespec tend;
    clock_gettime(CLOCK_MONOTONIC, &tstart);
    while (1) { 
         
        // -- start recording to file only when GStreamer process finishes loading 

        printf("Recoridng!\n");

        usecToSleep = 40000; //(1.0f/(25.0f)) * 1000000;
        
        clock_gettime(CLOCK_MONOTONIC, &tend);
        usecElapsed = getTimeSpecDiff(tstart,tend);
        //printf("usecElapsed %d\n", usecElapsed); 
        actualSleep = usecToSleep - usecElapsed;
        printf("ActualSleep %d\n", actualSleep);
        
        if (actualSleep > 0) {
            usleep(actualSleep); 
        }
        clock_gettime(CLOCK_MONOTONIC, &tstart);
    }
    
    return (EXIT_SUCCESS);
}