Monday, October 31, 2022

Simple C++ example to send serial AT commands to and receive data from a modem

I tried using many C/C++ libraries trying to coax a 5G modem to respond to my input AT commands for a long time but I was not successful. The command I was trying to send was the Quectel modem command to query for PDN channels:

AT+CGDCONT?

After a while, I figured out I had to simulate a keyboard Enter press in code to actually tell the modem the command is complete. So all I had to do was append the carriage return (\r) and new line (\n) characters to the AT command string, e.g:

string cmd = "AT+CGDCONT?\r\n";

A working C++ code example is shown below:

#include <string>
#include <iostream>
#include <cstdio>
#include <unistd.h>

// Using header only library from 
// https://github.com/karthickai/serial
#include "Serial.h"


using namespace std;


int main ( int argc, char** argv) {

        string commPort = "/dev/ttyUSB2";
        unsigned int baud = 115200;
        serial::Serial serial;

        serial.open ( commPort, baud);

        if ( !serial.isOpen()) {
                cout << "comm port is not open" << endl;
                return 1;
        }

        // A modem AT query command
        string cmd = "AT+CGDCONT?\r\n";
        vector<uint8_t> cmdVec (cmd.begin(), cmd.end());

        // send command to the modem
        size_t bytesSent = serial.transmitAsync(cmdVec);
        cout << "Bytes sent " << bytesSent << endl;

        int received_bytes = -1;

        while (received_bytes != 0 ) {
                // read one byte from the modem and timeout if the
                // there is no response in more than 1 sec.
                future<vector<uint8_t>> future = serial.receiveAsync(1, 1000);
                vector<uint8_t> const received_data = future.get();
                received_bytes = received_data.size();

                string str(received_data.begin(), received_data.end());
                cout << "[" << received_data[0] << "] " << endl;
        }
        // Close the serial port
        serial.close();

        cout << "End of process" << endl;

        return 0;

}

The example prints out the data sent by the modem to the calling program, as shown below:

Note: this example is using the modern serial header only C++ library from this site: https://github.com/karthickai/serial

Monday, October 24, 2022

QGIS unable to edit SpatiaLite database layer workaround

Recently I upgraded to QGIS 3.26 on Ubuntu 22.04 and found that I was unable to edit my existing SpatiaLite database layers, i.e. the Toggle Editing tool bar icon could not be enabled. The screenshot below illustrates the problem.

While I have not found the cause of the problem, I found a simple workaround to the issue: just create a new SpatiaLite database and import the layers from the old database. The newly imported database layers can be enabled for editing. 

 

Create a new SpatiaLite database

  1. In the Browser pane of QGIS, press the right mouse button on the SpatiaLite node.



    A pop up menu appear.

  2. Choose Create New Database.



  3. In the Name field, type in a new SpatiaLite database file name, e.g. new_rail.sqlite. Click Save.

    The database is created.


Import layers from the old SpatiaLite database to the new SpatiaLite database

  1. If the layer e.g. node from the old SpatiaLite database e.g. rail.sqlite is not displayed in QGIS, then add the layer to the map window.


  2. In the QGIS menu, select Database | DB Manager.

    The DB Manager dialog box appears.


  3. Expand the SpatiaLite Providers tree node and double click on the newly created SpatiaLite database, e.g. new_rail.sqlite.



  4. Click Import Layer/File.

    The Import vector layer dialog box appears.


  5. In the Input drop down list, choose the layer from the old database, e.g. nodes. In the Output Table field, type in a new table name, e.g. nodes. Click OK.

    The old layer is imported into the new database.

Displaying the newly imported Spatialite layer

  1. In QGIS, select Layer | Add Layer | Add SpatiaLite Layer.



  2. In the Connections drop down list, select the new SpatiaLite database, e.g. new_rail.sqlite. Click Connect.

  3. In the table list, select the new layer nodes. Click Add.

    The new nodes layer is displayed in the map window.


  4. Select the new layer nodes in the Legend pane.

    The Toggle Editing icon is enabled now.


Monday, October 17, 2022

Auto mount an SD card upon insert on Ubuntu Server

I have a headless Raspberry Pi board running Ubuntu 20.04.x server with a USB SD card reader. I wanted the system to automatically mount an SD card to a fixed mount point, e.g. /media/ubuntu/sdcard/ upon card insertion. By default, Ubuntu Server doesn't mount the SD card so I had to do some setup and configuration, as illustrate in the steps below. I had to install some prerequisite software package (udevil) and create a systemd service.

Install udevil package and create mount directory

  1. Open up a Terminal and type in the following command to install udevil.

    $ sudo apt install udevil

  2. Then, create a SD card directory mount point, e.g. /media/ubuntu/sdcard/ with the following command:

    $ mkdir -p /media/ubuntu/sdcard

 

Add a mount rule to the fstab file

  1. Using a text editor, e.g. vi, open up the system file /etc/fstab.

    $ sudo vi /etc/fstab

  2. Append the following line:

    /dev/sda1 /media/ubuntu/sdcard auto rw,user,exec,umask=000  0  2

    An example fstab file is shown below:
LABEL=writable  /        ext4   defaults        0 1
LABEL=system-boot       /boot/firmware  vfat    defaults        0       1
/dev/sda1       /media/ubuntu/sdcard auto rw,user,exec,umask=000        0       2

Create the devmon Systemd service

  1. In the Terminal, change directory to /etc/systemd/system/.

    $ cd /etc/systemd/system

  2. Using a text editor, e.g. vi, create a file e.g. devmon.service. Enter the following and save the file.

    [Unit]
    Description=Systemd service for running devmon
    [Service]
    Type=simple
    User=ubuntu
    Group=ubuntu
    ExecStart=/usr/bin/devmon
    [Install]
    WantedBy=multi-user.target
    


  3. In the Terminal, type in the following to generate the service:

    $ sudo systemctl daemon-reload
    $ sudo systemctl enable devmon

  4. Then either reboot the board or run the following command to start the service:

    $ sudo systemctl start devmon

 Monitor the service

  1. In a Terminal, type in the following to monitor the newly create devmon service when an SD card is inserted or unmounted.

    $ journalctl -u devmon -f

 Note: The SD card should be unmounted properly using the following command(s):

$ udevil umount /media/ubuntu/sdcard

- or -

$ sudo umount /media/ubuntu/sdcard