Connecting to a WiFi network on Linux at startup without GUI

Many embedded Linux platforms are expected to connect to a WiFi network at startup automatically. And the Linux system is booted into multi-user model, i.e., no GUI, X window, or desktop environment. A lot of the solutions, e.g., making a connection available for all users, floating on the Internet are for Linux systems that boot into a desktop environment. In those cases, the nm-applet, which has an icon sitting in the tray area of the desktop environment, handles the connection to WiFi.

The discussion below applies to Ubuntu 15.04+, which uses systemd to manage initialization process.

Background

NetworkManager is a daemon that manages networking devices on Linux systems. It has become the de facto network managing program for Linux since its introduction in 2004. See more at Wikipedia: https://en.wikipedia.org/wiki/NetworkManager

Since it is a daemon, users interact with it via other applications. On most modern Linux distributions, you see an icon on the tray area, that once clicked, will give you a list of WiFi network IDs (SSIDs). This application is called nm-applet. NetworkManager also has a command line interface called nmcli.

Easy solution: Connect once and remember.


NetworkManager will automatically connect to a WiFi network that was connected to previously. So, you can connect to a WiFi network under a desktop environment using nm-applet, or use command line (see below), then boot into no-GUI (e.g., system level 3) forever, and enjoy the same WiFi network forever.

If you've connected to multiple WiFi networks before, it's very hard to say which one will be connected to in the future, supposing they will all be available. Most likely, the very last one connected to will be the top candidate. If unsure, see the next two sections.

In case you need to connect to a WiFi network via command line

There are many cases that you want to specify which WiFi network to connect to, e.g., the first time or you want to switch the network. The command to connect to a WiFi SSID uses the following syntax:
nmcli device wifi connect $SSID
For example,
nmcli device wifi connect Haha
where H360 is the SSID.

Specifying the WiFi network to connect to at startup


As mentioned earlier, if you have connected to multiple WiFi networks before, NetworkManager will use an algorithm, which is unfortunately unclear and thus underterministic to me, to pick one (assuming that they are all still available) the next time. To ensure to connect to a particular network, you can create a startup service for systemd, the initialization manager of modern Linux, to be executed at system startup.

Let's call the service mywifi. Create a file called mywifi.service under
/etc/systemd/system
with content like this:
[Unit]
Description=Connect to Haha

[Service]
ExecStart=/usr/bin/nmcli device wifi connect 

[Install]
WantedBy=multi-user.target

As you can see, the command to connect to a network called H360 is to be executed at startup, only when the system is booting into multi-user level (see the Red Hat doc for levels.)

Then enable this service and finally reboot
systemctl enable mywifi

By "enable", we mean automatically running this service at startup without logging in.

Troubleshooting: device unavailable

I had some rare cases that a WiFi interface device is unavailable. Simply stop and restart the NetworkManager service may solve the problem:
systemctl stop NetworkManager
systemctl start NetworkManager

Some examples for using nmcli

forrest@A785GM-M:~$ nmcli device status
DEVICE           TYPE      STATE        CONNECTION 
wlx001478962ea6  wifi      connected    Haha 4     
en2s0            ethernet  unavailable  --         
lo               loopback  unmanaged    --         
forrest@A785GM-M:~$ nmcli general status
STATE      CONNECTIVITY  WIFI-HW  WIFI     WWAN-HW  WWAN    
connected  full          enabled  enabled  enabled  enabled 
forrest@A785GM-M:~$ nmcli radio wifi
enabled
forrest@A785GM-M:~$ nmcli dev wifi
*  SSID            MODE   CHAN  RATE       SIGNAL  BARS  SECURITY  
*  Haha            Infra  1     54 Mbit/s  100     ▂▄▆█  WEP          
   TP-LINK_6DFF    Infra  5     54 Mbit/s  100     ▂▄▆█  WPA1 WPA2 
   --              Infra  10    54 Mbit/s  100     ▂▄▆█  WPA2      
*  Haha            Infra  1     54 Mbit/s  99      ▂▄▆█  WEP     

References

Solution to most not-found or undefined problems in C/C++ compilation

Many people have compilation errors. If you search "undefined reference to" in Google, or maybe StackOverflow alone, you will notice that those problems have been asked dozens if not hundreds of times. There are generally 4 problems:
  • was not declared in this scope
  • no such a file or directory
  • undefined reference to
  • error while loading shared library, or cannot open shared object file

The thing is actually very simple: the compiler is not told to find the right file at the right place. The discussion below is based on Linux using GCC for C and C++.

Problem 1: was not declared in this scope

Usually, this problem has nothing to do with your system settings or compiler configuration. It's a problem within your source code. On the top level, the function is not defined. The reason can vary.

One reason is that the file declaring (or defining, if the declaration is done together with definition) the functions is not specified, e.g., the header file is not included. This can be easily fixed by including such a file in your source code.

Another reason is that you used the function beyond its class or namespace.

Problem 2: No such a file or directory for a header file


If a header file is not found, the preprocessor or the preprocessor part of the compiler, will raise the error No such a file or directory. Depends on where the header files are supposed to be at, there are different solutions.

Most C/C++ compiles treat the quote form of #include and angle-bracket form of #include differently. Simply running
$ echo | gcc -E -Wp,-v -
will tell you the difference:
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/5/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include

Those directories listed under the section #include <...> search starts here: are referred to as standard system directories [1]. They are from the convention of Linux systems' file hierarchy.

If you want to expand the search for angle-bracket form #include<...>, use the -I option. The preprocessor will search header files in directories followed by -I option before searching system directories. Hence, a header file in a -I-option directory will override its counterpart in system directories and be used to compile your code.

For quote form #include"...", the preprocessor will first search in the current directory that the source file is at and then all the directories specified after the -iquote option.

The example below will show you how -I and -iquote append search paths:
l$ echo | gcc -iquote/home/forrest/Downloads -I/home/forrest/Dropbox -E -Wp,-v -
#include "..." search starts here:
 /home/forrest/Downloads
#include <...> search starts here:
 /home/forrest/Dropbox
 /usr/lib/gcc/x86_64-linux-gnu/5/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include

So, if you have the following macro in your code called mycode.c:
#include < xyz.h>
#include "abc/xyz.h"
and your GCC command is like this
gcc mycode.c -I/opq/ -iquote/uvw
then the GCC's preprocessor (again, it's part of the GCC) will look for header files in the following full paths (on Ubuntu Linux 16.04) in addition to the search in system directories:
/opq/xyz.h
 /uvw/abc/xyz.h
According to GCC document[1], it searches for header files in quote form #include"..." before searching for angle-bracket form #include"...".

Further specifications can be achieved using two other options -isystem and -idirafter. They are all well documented in GCC document[2].

Problem 3: Undefined reference to

You probably see an error like this
face.cpp:(.text._ZN2cv3Mat7releaseEv[_ZN2cv3Mat7releaseEv]+0x4b): undefined reference to `cv::Mat::deallocate()'
collect2: error: ld returned 1 exit status
This is a link-time error when the linker ld, which is called automatically when you use GCC, cannot find the binary library that contains at least one function called by your code.

To fix, first tell the compiler (actually it's linker part) the path containing library files using the -L option [2] and then library file names using the -l option [4]. If we denote the values after -l and -L options as X and Y respectively, the compiler will search for every file called libY.so under every directory X. That's why on (almost) all Linux systems, a shared library file begins with lib and ends with the appendix .so, such as libopencv_core.so.

For example, the command
$ g++ face.cpp -L/opencv/lib -lopencv_core -lopencv_videoio
will ask the linker to find the following binary library files:
/opencv/lib/libopencv_core.so
/opencv/lib/libopencv_videoio.so

Note that you may not be able to use some shell directives, such as ~ after the -L option.

In most cases, you do not need to use the -L and -l options because the compiler (again, its linker part) automatically searches in a set of system directories. When you have to, some tools can help you, such as pkg-config. I will write another blog post.

Problem 4: error while loading shared libraries or cannot open shared object file


Now, your program has been successfully compiled. When running it, you probably see an error like this:
./a.out: error while loading shared libraries: libopencv_core.so.3.3: cannot open shared object file: No such file or directory 
This problem is from the loader. Unlike the link-time error above, this problem is a run-time error. The shared library filenames are usually hardcoded into your binary program. To fix it, simply tell the loader where to find the specified shared library files. There are multiple ways.

Solution 1: Most Linux systems maintain an environment variable LD_LIBRARY_PATH and the loader will search for binary library files requested by a program in all directories listed in LD_LIBRARY_PATH, on top of a set of standard system directories, such as /lib or /usr/lib. To set LD_LIBRARY_PATH is like how you set any other environment variables, e.g., export on the Shell or edit and source ~/.bashrc.

Solution 2: Most Linux systems also maintains a shared library cache by a program called ld-config. It remembers where to find the default location of each shared library file. Simply run
$ ldconfig -p
it will tell you the mapping from shared library files to absolute paths in the system, e.g.,
2591 libs found in cache `/etc/ld.so.cache'
 libzzipwrap-0.so.13 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzzipwrap-0.so.13
 libzzipmmapped-0.so.13 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzzipmmapped-0.so.13
 libzzipfseeko-0.so.13 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzzipfseeko-0.so.13

To change the mapping, simply edit /etc/ld.so.conf and then run
$ sudo ldconfig
to rebuild the cache.

Solution 3: The two changes above are applied system-wide. A more flexible way is to specify the location of the shared libraries when linking your code using the
-Wl,-rpath
option, e.g.,
g++ face.cpp -idirafter ~/Downloads/opencv_TBB_install/include -L/home/forrest/Downloads/opencv_TBB_install/lib -lopencv_core -lopencv_objdetect -lopencv_highgui -lopencv_imgproc -lopencv_videoio -Wl,-rpath=/home/forrest/Downloads/opencv_TBB_install/lib

The disadvantage of Solution 3 is that if you change the location of the shared library, then you will run into error again.


References:
[1] Filesystem Hierarchy, https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard
[2] GCC options for directories https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html
[3] Shared Library How-To, http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
[4] GCC options for linking, https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

The correct dimensions of Hitec C1 spline

The C1 spline of 24 teeth is the standard shaft size for Hitec's servo motors, which are quite popular in maker community. Recently, I needed to design some parts that fits with it. However, finding it's accurate inner (basic) and outer (tip or addendum) diameters is very difficult. I don't know why this company does not simply release a drawing of it.

Some info floating on the Internet is not really accurate, such as this one: www.robotshop.com/blog/en/hobby-robot-industry-standards-hubs-patterns-18681

In the end, I reverse engineered from a design shared on GrabCAD and get the dimensions that work: https://grabcad.com/library/servo-horn-with-c1-spline-1

Here we go:
  • Inner radius: 2.520 mm
  • Outer radius: 2.845 mm. 
If you wanna 3D print an inner gear (a.k.a., annular gear) that fits with C1 spline, be sure to take the error or expansion that 3D printing can produce into consideration. In my case, I increased both inner and outer radii by 0.15 mm to 2.670 and 2.995mm respectively. 

Creating an arbitrary surface or any geometric element in FreeCAD

Creating an arbitrary geometric element, such as a surface or a polygon, is a task common in CAD.

FreeCAD provides two ways to do so in its Part module.

The first approach is called Create Primitives, where you specify the parameters to define a geometric element. The menu is Part -> CreatePrimitives. See https://www.freecadweb.org/wiki/Part_CreatePrimitives

An example is below.



The second approach is called Shape Builder, where you build a new shape from elements defined earlier, such as creating a face from edges or vertexes. The menu is Part -> Shapebuilder. See https://www.freecadweb.org/wiki/Part_Shapebuilder

A good reference of all functions that Part module provides can be found here https://www.freecadweb.org/wiki/Part_Module

Population of Northeast Ohio

Let's just look at some data of the population of northeast Ohio (NEO).

This is what the population distribution in NEO looks like:




In contrast, this is what the overall US population distribution looks like:




NEO is older than the other two big metros in Ohio, Cincinnati and Columbus:







This is what Dallas looks like:




The average ages in Cleveland, Cincinnati, and Columbus are: 41.1, 37.9, and 35.7. As a comparison, the average age of Dallas is 34.2. Cleveland population is 7 years older than that of Dallas.

The fertility rate of Cleveland, Cincinnati, and Columbus are: 4.8%, 5.2%, and 5.4%. As a comparison, the fertility rate of Dallas is 5.4%.

Hence, NEO has fewer women at birth age (15 to 50) than the national average and its fertility rate is under national average. The result? Fewer babies.