Skip to content

Lidar Mapping (PoC) »

Base Stattion on Raspberry Pi 3 B+ board

Last update: 2022-06-29

Operating System#

  1. Download Raspberry Pi Imager and install it.

  2. Run the Pi Imager, and select the Desktop version but without recommended software.

  3. Press Ctrl + Shift + X to show the advanced menu, fill some settings as below:

    • Hostname: base
    • SSH: yes
      • Default username: pi, with password raspberry
    • WiFi (optional): set SSID and password
Alternative method using balenaEtcher
  1. Download Raspberry Pi OS image, select Desktop version but without recommended software.

  2. Download balenaEtcher and install it, and write the Image in an SD Card. After finishing, re-plug the SD Card.

  3. Initial settings:

    • Default hostname: raspberrypi.local

    • Add an empty file with name ssh into the boot partition to enabled SSH.

      • Default username: pi, with password raspberry
    • WiFi (optional): Enabling WiFi network by add a file wpa_supplicant.conf in the boot partition:

      ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
  1. Some tweaks can be applied after login

    • To use some list command, in .bashrc, enable alias for ls command.

    • To disable animation on GUI:

      gsettings set org.gnome.desktop.interface enable-animations false
    • If eth0 is used, it may take priority of network connection over the wlan0. If internet is accessible through wlan0, edit /etc/dhcpcd.conf and add this:

      interface wlan0
      metric 100

      Then wireless will have precedence, its default route/gateway will be used.

  2. Enable GPIO and peripherals

    • The official tool raspi-config should be used to configure the Pi:

      sudo raspi-config

      Select Interfacing Options and select below item:

      • SPIEnable
      • I2CEnable
      • Serial PortNo login shellEnable

      After enabling the primary UART, the VPU clock will be set at 250 MHz. CPU clock will not be affected. We aren’t going to use VPU, so it is acceptable to reduce its clock.

    • Run groups to see the current user is added into groups for using GPIO including gpio, i2c and spi.

Raspberry Pi 40-pin header

UART Config#

Refer to Raspberry Pi UART.

There are two types of UART available on the Raspberry Pi - PL011 and mini UART. The PL011 is a capable, broadly 16550-compatible UART, while the mini UART has a reduced feature set. All UARTs on the Raspberry Pi are 3.3 V only.

The Raspberry Pi 3 has below default UART configuration:

Port Type Usage Pin Map Linux Device
UART0 PL011 Secondary (Bluetooth), Enabled N/A /dev/serial1
UART1 mini UART Primary, Disabled GPIO 14 (TX), GPIO 15 (RX) /dev/serial0

That means the exported UART port on GPIO header is a mini-UART. The problem is the speed of mini-UART port depends on VPU (graphic) Core Frequency.

The primary UART is disabled by default. It has to be enabled by running raspi-config to set up interfaces.

Use PL011 on the primary UART via Device Tree overlay

The two most useful overlays are disable-bt and miniuart-bt which can be added config.txt file, such as:


The option disable-bt disables the Bluetooth device and makes the first PL011 (UART0) the primary UART. You must also disable the system service that initializes the modem, so it does not connect to the UART:

sudo systemctl disable hciuart

To get rid of using sudo permission, add current user into the serial group.

Run ls -al /dev/tty* to check the user group of ttyS0, ttyTHS1. Normally, it is needed to add current user into tty and dialout groups:

sudo usermod -a -G tty $USER && \
sudo usermod -a -G dialout $USER && \
sudo reboot
UART Testing

For testing, a serial terminal must be installed. Choose one of below.


sudo apt install -y putty

Follow GUI to run.


sudo apt install -y minicom
minicom -D /dev/ttyS0 -b 115200

Press Ctrl-A X to exit.


sudo apt install -y picocom
picocom /dev/ttyS0 -b 115200

Press Ctrl-A and Ctrl-X to exit.


sudo apt install -y screen
screen /dev/ttyS0 115200

Press Ctrl-A K to exit.

Module Connection#

Refer to below pinout diagram:

Base wiring

Serial driver#

Download source code and build:

git clone && \
cd SerialPort && \
make && \
sudo make install

An example to communicate with Serial port:

// Serial library
#include "serial/SerialPort.h"
#include <unistd.h>
#include <stdio.h>

#define SERIAL_PORT "/dev/ttyS0"

int main( /*int argc, char *argv[]*/) {
    SerialPort serial;

    char errorOpening = serial.openDevice(SERIAL_PORT, 115200);
    if (errorOpening!=1) return errorOpening;
    printf ("Successful connection to %s\n",SERIAL_PORT);

    // Display ASCII characters (from 32 to 128)
    for (int c=32;c<128;c++)

    // Read lines and print them out
    char line[1024];

    while(1) {
        int n = serial.readBytes(line, sizeof(line));
        if (n>=0) {
            std::cout << std::string(line, n) << std::endl;

    // Close the serial device

    return 0 ;

Compile and run:

g++ example.cpp -lserial -o example

nRF24 driver#

Download source code from GitHub and build:

git clone && \
cd RF24 && \
./configure --driver=SPIDEV && \
make && \
sudo make install

SPIDEV must be used to avoid permission error when using default RPi BCM driver

Work with the original source code

GitHub at
The guide to install in Linux at

Download the file:


Make it executable:

chmod +x

Run it and choose the option:

  • RF24 Core
  • SPIDEV driver
Do you want to install GIT using APT (Used to download source code) [y/N]? n
Do you want to install the RF24 core library, [y/N]? y
Do you want to install the RF24Network library [y/N]?
Do you want to install the RF24Mesh library [y/N]?
Do you want to install the RF24Gateway library [y/N]?

Installing RF24 Repo...

__* Install RF24 core using? *__
1.BCM2835 Driver(Performance) 2.SPIDEV(Compatibility, Default)
3.WiringPi(Its WiringPi!) 4.MRAA(Intel Devices) 5.LittleWire
[Installing Libs to /usr/local/lib]
[Installing Headers to /usr/local/include/RF24]
make: Leaving directory '/home/pi/rf24libs/RF24'

Source to test data receiving. Check the transferring site in Rover.

#include <iostream>    // cin, cout, endl
#include <RF24/RF24.h>

// create RF24 instance
RF24 radio(24 /* CE = sys_gpio_24 */,
            0 /* CSN = 0 means spidev0.0 */
            /* default speed is 10 Mbps */);

// max payload of RF24 is 32 bytes
uint8_t payload[32];

int main(int argc, char** argv) {
    // perform hardware check
    if (!radio.begin()) {
        std::cout << "radio hardware is not responding!!" << std::endl;
        return 0; // quit now

    radio.setChannel(100); // 2400 + 100 = 2500 MHz, out of WiFi band

    // address, defaut length is 5
    uint8_t tx_address[6] = "1Addr"; // write to
    radio.openWritingPipe(tx_address); // always uses pipe 0

    // (smaller) function that prints raw register values
    // (larger) function that prints human readable data

    // Start
    std::cout << "Start TX" << std::endl;
    radio.stopListening(); // put radio in TX mode

    while(true) {
        radio.write(&payload, 32); // transmit

Compile the source code:

arm-linux-gnueabihf-g++ -marm -march=armv6zk -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard -Ofast -Wall -pthread  rf24_tx.cpp -lrf24 -o rf24_tx

or just use:

g++ -Ofast -Wall -pthread  rf24_tx.cpp -lrf24 -o rf24_tx

Run it and see the log:

================ SPI Configuration ================
CSN Pin         = /dev/spidev0.0
CE Pin          = Custom GPIO24
SPI Speedz      = 10 Mhz
================ NRF Configuration ================
STATUS          = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
RX_ADDR_P0-1    = 0x7264644131 0x65646f4e31
RX_ADDR_P2-5    = 0xc3 0xc4 0xc5 0xc6
TX_ADDR         = 0x7264644131
RX_PW_P0-6      = 0x20 0x20 0x20 0x20 0x20 0x20
EN_AA           = 0x3f
EN_RXADDR       = 0x03
RF_CH           = 0x64
RF_SETUP        = 0x03
CONFIG          = 0x0e
DYNPD/FEATURE   = 0x00 0x00
Data Rate       = 1 MBPS
Model           = nRF24L01+
CRC Length      = 16 bits
PA Power        = PA_LOW
ARC             = 0
================ SPI Configuration ================
CSN Pin                 = /dev/spidev0.0
CE Pin                  = Custom GPIO24
SPI Frequency           = 10 Mhz
================ NRF Configuration ================
Channel                 = 100 (~ 2500 MHz)
RF Data Rate            = 1 MBPS
RF Power Amplifier      = PA_LOW
RF Low Noise Amplifier  = Enabled
CRC Length              = 16 bits
Address Length          = 5 bytes
Static Payload Length   = 32 bytes
Auto Retry Delay        = 1500 microseconds
Auto Retry Attempts     = 15 maximum
Packets lost on
    current channel     = 0
Retry attempts made for
    last transmission   = 0
Multicast               = Disabled
Custom ACK Payload      = Disabled
Dynamic Payloads        = Disabled
Auto Acknowledgment     = Enabled
Primary Mode            = TX
TX address              = 0x7264644131
pipe 0 ( open ) bound   = 0x7264644131
pipe 1 ( open ) bound   = 0x65646f4e31
pipe 2 (closed) bound   = 0xc3
pipe 3 (closed) bound   = 0xc4
pipe 4 (closed) bound   = 0xc5
pipe 5 (closed) bound   = 0xc6
Start TX

OLED driver#

Download source code and build:

git clone && \
cd OLED_SSD1306_I2C_Linux && \
make && \
sudo make install

Write a simple app to a progress bar with label and numeric value:

#include <string.h>
#include <unistd.h>
#include <SSD1306/ssd1306.h>

int main() {
    char counter = 0;
    char buffer[3];
    while(1) {
        sprintf(buffer, "%d", counter++);
        SSD1306_WriteString(0,0, "counter:", &Font_7x10, SSD1306_WHITE, SSD1306_OVERRIDE);
        SSD1306_WriteString(0,10, buffer, &Font_11x18, SSD1306_WHITE, SSD1306_OVERRIDE);
    return 0;

Compile and run:

gcc progress_bar.c -lssd1306 -o progress_bar && \

Auto-mount USB#

Check out and install:

git clone && \
cd USB_Automount && \

Plugged-in USB will be mounted into /media/<Label> or /media/<sdXy>.

Auto-start application#

Refer to Rover setup.