Introduction

The CCpilot V1000 and V1200 has variants with built-in Wifi & Bluetooth, enabling software updates over the air, smartphone integration and other wireless features. This document will provide an introduction to Wifi and what tools to use in different scenarios.

Overview

Glossary

Word/Abbrevation Explanation
IEEE 802.11 MAC and PHY layer specifications for implementing wireless local area network communication
MAC Medium Access Control
PHY Physical layer
Wi-Fi Wireles Fidelity, Wi-Fi is also a trademark of the Wi-Fi Allicane
ISM Industrial Scientific Medical, the frequency band at 2.4GHz that is used for wifi
OFDM Orthogonal frequency division multiplexing, a technique that increased the raw data rate to 54Mbit/s in the 5GHz frequency band
AP Access point, a device providing wireless connectivity
BSS Basic Service Set, a closed group of devices with a central access point
BSSID Unique id of the AP (mac address)
SSID Service Set Id, is the network name of a BSS
BSA Basic Service Area, the physical border of the area the AP antennas work within
CELL Another word for BSA
DS Distribution System, the link between an AP and a wired network, permits wireless clients to talk to a wired network
ESS Extended Service Set, multiple AP cover a larger area using the same SSID and are usually connected to the same switch, permits roaming
ROAMING Clients seamlessly switch between cells in the same ESS without losing connectivity, very much like how mobile phones work
IBSS Independent Basic Service Set, Ad hoc / Wi-Fi Direct, clients communicate directly with each other without any AP
OSI model Open Systems Interconnection model, describes the universal standard of communication functions of a computing system
STA Station, a computer with a wireless network interface controller
WEP Wifi Protected Privacy, old security protocol
WPA1/2/3 Wifi Protected Access, successor to WEP with better security
WPA-Personal For home and small business
WPA-PSK Another word for WPA-Personal
WPA-Enterprise For enterprise networks, needs an authentication server
WPA-802.1X Another word for WPA-Enterprise
NIC Network Interface Card
DNS Domain Name System
DHCP Dynamic Host Configuration Protocol

Wifi

Also Wi-Fi which is a trademark of Wi-Fi Alliance, is a family of protocols based on the IEEE 802.11 family of standards.

History

1985 - In USA parts of the ISM radio bands were released for unlicenced communication. The same range of frequency bands are used by other equipment such as microwave ovens, baby monitors, cordless telephones and bluetooth devices.

1991 - NCR corp and AT&T corp invented WaveLan to be used in cashier systems.

1992 - The radiophysics division of CSIRO in Australia built a prototype test bed for wireless local area network.

1997 - The first version of 802.11 protocol was released with a link speed of 2Mbit/s.

1999 - The updated 802.11b protocol was released which permits a link speed of 11MBit/s.
Also the Wi-Fi allicance were formed as a trade association to hold the Wi-Fi trademark under which most products are sold.
Apple made Wi-Fi popular at this time when using it in the iBook laptops. The first mass produced consumer product with Wi-Fi. Apple branded it as AirPort.

Further down the road

Generation IEEE Standard Maximum Linkrate (Mbit/s) Adopted Radio Frequency (GHz)
Wi-Fi 7 802.11be 40000 TBA 2.4/5/6
Wi-Fi 6/6E 802.11ax 600 to 9608 2019/2020 2.4/5/6
Wi-Fi 5 802.11.ac 433 to 6933 2014 5
Wi-Fi 4 802.11n 72 to 600 2008 2.4/5
Wi-Fi 3 802.11g 6 to 54 2003 2.4
Wi-Fi 2 802.11a 6 to 54 1999 5
Wi-Fi 1 802.11b 1 to 11 1999 2.4
Wi-Fi 0 802.11 1 to 2 1997 2.4

Technique

Using radio techniques to modulate/demodulate a carrier signal (with the modulation in the sidebands). For the first version of wifi they used a number of 5MHz wide bands from 2400MHz up to 2465MHz (13 channels). Often standards allow for channels to be bonded together for higher throughput so a transmitter normally occupy at least 20MHz. New standars also allow for wider channels.
Wi-Fi 4 permits more antennas to be used which boosts speeds and range. The maximum power of the antennas is locally regulated. Within the EU the equivalent isotropically radiated power(EIRP) is limited to 20dBm (100mW).

Modes

Infrastructure

All traffic goes through the same base station. As long as the communicating devices can reach the base station they can communicate with each other. It is used to provide communication between wireless clients and wired network resources through an access point. The basic setup with one access point and a number computers connected wirelessly is called a BSS (Basic Service Set) which forms a cell.

Infrastructure

Multiple access points

Using ESS (Extended service set) you can make a network with multiple access points using the same SSID, combining a number of BBSs o and ESS. Wifi client devices can then connect to the access point with the strongest signal using the same security settings. This is also known as roaming network and usually has a central controller which can administrate all access points. The access points are connected together through a distribution system (wired or wireless).

Ad hoc / Wi-Fi direct

Devices communicate directly without using a base station (router). This is popular among hand held game consoles for multiplayer games and also other consumer electronics like digital cameras and printers. Some devices can also share internet via ad hoc networks. A drawback is that two devices, lets call them A and B, much be in range of each other to be able to communicate with each other even with a third device C (within range of A and B) which would not automatically route traffic between A and B.

Infrastructure

OSI model

The OSI model is used to describe networking. Each layer handles different aspects of networking and only exchange information with the layer below or above itself. The wifi standard 802-11 only deals with the two lowest layers of the OSI reference model.

OSI model

Security

The first protocol called WEP (Wired equivalent Privacy) popular around 2000 and might still be used today has poor security. Using only 64-bit encryption and with other flaws made it easy to hack.

With WPA (Wifi protected access) they fixed some of the problems with WEP but there were still vulnerabilities. A big improvement were the use of TKIP (Temporal Key Integrity Protocol) to dynamically change a 128-bit per packet key.

WPA2 ratified in 2004 replaced WPA. All new devices needed to be WPA2 certified between 2006 and 2020 to bear the Wi-Fi trademark. AES (Advanced Encryption Standard) improved upon TKIP in WPA.

WPA3 announced in 2018 as a replacement for WPA2. Replaces the PSK (Pre-Shared Key) used in WPA2 with a more secure SAE(Simultaneous Authentication of Equals) method for initial key exchange in personal mode.

WPA-personal versus WPA-enterprise. WPA-personal (also referred to as WPA-PSK) is made for home or small office networks that doesn’t require an authentication server. In personal the network traffic is encrypted by a 128-bit key derived from a 256-bit shared key. WPA personal is available in all three versions av WPA. WPA enterprise on the other hand (also referred to as WPA-802.1X) are designed for enterprise networks and requires a RADIUS authentication server. Various kinds of EAP (Extensible Authentication Protocal) are used for authentication making it more secure than the person variant. WPA enterprise is also available in the three versions of WPA.

WPS (Wifi Protected Setup) is an alternative authentication method using a physical button or a PIN code to get access. It has a major security hole in the WPS PIN recovery attack. WPA3 has another way for devices that lack sufficient user interface capabilities to connect.

Tools/Software

A short description of useful tools when using wifi on a CCpilot display. The following tools except for dhclient are included from OS version 2.0.8.1.

NetworkManager

Very popular tool for managing network connections in modern Linux. Introduced by Red Hat in 2004 to simplify and automate network configuration, especially wireless connections. The NetworkManager is run as a systemd service and uses D-Bus to detect and configure network interfaces. It has a D-Bus interface to be used if you are coding a network app/program. See code example below on using NetworkManagerQt.

nmcli

The command line interface for NetworkManager. A very good tool for configurating network settings. It is easy to use and you can use SSH to connect to a remote device and then use nmcli for administration tasks. A lot of examples below uses nmcli.

wpa_supplicant

The wpa_supplicant handles security in wireless connections. NetworkManager will call wpa_supplicant via its D-Bus interface to use security. You can configurate wpa-supplicant manually via config files but the normal way is to let NetworkManager call wpa-supplicant via D-Bus.

wpa_cli

The command line interface for wpa-supplicant. If you decide not to use NetworkManager at all, this is a good tool for administrating the wireless security and wireless connection. Simpy type wpa_cli in the terminal to open up the interactive tool.

Dnsmasq

Is a software providing DNS caching and a DHCP server.

Hostapd

Software used for setting up an access point with lots of configuration options. To hand out ip addresses the DHCP server in dnsmasq will be used.

iw

A command line interface tool for configuring wifi. It is useful for quickly getting information about your wireless device. For example typing iw list

dhclient

Needed if you don’t use NetworkManager which has an in-built dhclient. For example if only using wpa-supplicant without NetworkManager you wouldn’t get an IP address after connecting to a wireless network.

Installation guide

Supported platforms

  • i.MX 8

    • CCpilot V1000

    • CCpilot V1200

Wifi scenarios

Examples using command line tools

Enable wifi

nmcli radio wifi on

Scan for devices

Produces a nice list of nearby access points

nmcli dev wifi list

Scan for devices

Connect to an unprotected network

nmcli dev wifi connect "ssid"

Connect to a WPA2 encrypted network

Using –ask nmcli will prompt you for entering the password

nmcli --ask dev wifi connect "ssid"

Or submit password directly

nmcli dev wifi connect "ssid" password abcd1234

Commonly used commands with nmcli

Command Comment
nmcli con Show available connections
nmcli con show "name" Show details of a connection
nmcli con edit "name" Open up interactive edit of connection
nmcli con up "name" Activate connection
nmcli con down "name" Deactivate connection
nmcli con delete "name" Delete connection configuration

Setup access point / hotspot using hostapd

It looks like hostapd doesn’t need NetworkManager nor wpa_supplicant. An active wlan connection will prevent hostapd from working correctly.

To give clients IP addresses you need to configure dnsmasq. Usually something like this is needed in /etc/dnsmasq.conf

dhcp-range=192.168.1.50,192.168.1.150,12h

Configuration file for hostapd at /etc/hostapd.conf.

Important settings below. Every setting has detailed information in the hostapd.conf file.

interface=wlan0
driver=nl80211
ctrl_interface_group=0
ssid=YourHotspotName
hw_mode=g
channel=1
auth_algs=3
ignore_broadcast_ssid=0
ieee8021x=0
eapol_key_index_workaround=0
eap_server=0
own_ip_addr=127.0.0.1

To use WPA2 which has a lot better security than WPA1 set.

wpa=2
wpa_passphrase=yourhardtoguesspassword
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP

To hide your ssid from being scanned set

ignore_broadcast_ssid=1

Make sure you don’t have any active connections on wlan0 before initiating the Soft AP. Here is an example script for starting up hostapd setting your device IP to 192.168.1.50

#!/bin/bash
# Usage: ./initSoftAP
############### Initial wifi interface configuration #######
ip link set wlan0 down
ip addr flush dev wlan0
ip link set wlan0 up
ip addr add 192.168.1.50/24 dev wlan0

sleep 3

### Start dnsmasq ###
if [ -z "$(ps -e | grep -v grep | grep dnsmasq)" ]
then
 echo "Starting dnsmasq"
 dnsmasq
fi

### Enable NAT / Share internet via eth0 ###
iptables -F
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT

### Start hostapd ###
hostapd /etc/hostapd.conf

Wifi code examples

In the following examples we use C++ with Qt and its D-Bus interface classes. You can of course use other programming languages. KDE has made a D-Bus Qt NetworkManager package that simplifies using the D-Bus interface to NetworkManager.

Install NetworkManagerQt

Get the latest version from KDE NetworkManagerQt reference and build it using cmake. If you have problem building it please contact support@crosscontrol.com.

Use NetworkManagerQt code example

Using the KDE NetworkManagerQt framework we get access to all features exposed by NetworkManager on the D-Bus. Below is an example for scanning and connecting to wireless networks.

Idea

  • Show a list of scanned networks

  • Show active connection in list

  • Long press an access point to activate connection

  • If no config for SSID, show an “enter password” pop up

  • If entered password is wrong, remove connection

Creating a new connection when entering password will bind it to SSID, not a specific access point (BSSID) letting the connection roam between access points with the same SSID.
If long pressing an access point which we have a connection for. Bind connection to that specific access point.

UI

A simple UI with a button to trigger rescan of nearby access points. Main screen Main

If long pressing on an AP we don’t have a configurated connection to. Pop up a password box. Enter pw

Clicking in input area will pop up a virtual keyboard Typing

Displaying information Info

Showing active connection Active

Code snippet scan for networks

After initializing we have a pointer to the wireless device

NetworkManager::WirelessDevice::Ptr _wifiDevice;

Simply calling requestScan with an empty map

_wifiDevice->requestScan({});

Code for displaying / handling an access point

Using the Q_PROPERTY macro you can easily expose values to the UI (qml part). Since KDE didn’t provide that we created a simple wrapper class around their counterpart.

accespointwrapper.h

#ifndef ACCESSPOINTWRAPPER_H
#define ACCESSPOINTWRAPPER_H
#include <NetworkManagerQt/AccessPoint>

// Wrapper for KDE's AccessPoint class
// Now easy to show in UI
class AccessPointWrapper : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int signalStrength READ signalStrength NOTIFY signalStrengthChanged)
    Q_PROPERTY(QString hardwareAddress READ hardwareAddress CONSTANT)
    Q_PROPERTY(QString ssid READ ssid CONSTANT)
    Q_PROPERTY(QString maxBitRate READ maxBitRate NOTIFY maxBitRateChanged)
    Q_PROPERTY(QString security READ security CONSTANT)
    Q_PROPERTY(uint frequency READ frequency CONSTANT)
    Q_PROPERTY(bool connected READ connected WRITE setConnected NOTIFY connectedChanged)

public:
    explicit AccessPointWrapper(QObject *parent = nullptr);
    void init(const QString path);
    int signalStrength();
    QString hardwareAddress();
    QString ssid();
    QString maxBitRate();
    QString security();
    uint frequency();
    bool connected();
    void setConnected(bool connected);
    QString uni();
    NetworkManager::AccessPoint* AccessPoint();

signals:
    void signalStrengthChanged();
    void maxBitRateChanged();
    void connectedChanged();

public slots:
    void newSignalStrength(int newValue);
    void newBitRate(int newValue);

private:
    NetworkManager::AccessPoint *_accessPoint;
    bool _connected;

};

#endif // ACCESSPOINTWRAPPER_H

accesspointwrapper.cpp

#include "accesspointwrapper.h"
#include <qdebug.h>
#include <NetworkManagerQt/Manager>

AccessPointWrapper::AccessPointWrapper(QObject *parent) : QObject(parent)
{
    _connected=false;    
}

void AccessPointWrapper::init(const QString path)
{
    // Create accesspoint instance and connect signals
    _accessPoint = new NetworkManager::AccessPoint(path, this);

    connect(_accessPoint, SIGNAL(signalStrengthChanged(int)), this, SLOT(newSignalStrength(int)));
    connect(_accessPoint, SIGNAL(bitRateChanged(int)), this, SLOT(newBitRate(int)));
}

int AccessPointWrapper::signalStrength()
{
    return _accessPoint->signalStrength();
}

QString AccessPointWrapper::hardwareAddress()
{
    return _accessPoint->hardwareAddress();
}

QString AccessPointWrapper::ssid()
{
    return _accessPoint->ssid();
}

QString AccessPointWrapper::maxBitRate()
{
    return QString::number(_accessPoint->maxBitRate()/1000) + " Mbit/s";
}

QString AccessPointWrapper::security()
{
    QString ans = "";
    if (_accessPoint->wpaFlags() > 0)
        ans.append("WPA1 ");
    if (_accessPoint->rsnFlags() > 0)
        ans.append("WPA2 ");
    if (_accessPoint->rsnFlags() & NetworkManager::AccessPoint::KeyMgmtSAE)
        ans.append("WPA3 ");
    if (_accessPoint->rsnFlags() & NetworkManager::AccessPoint::KeyMgmt8021x)
        ans.append("8021x");

    return ans;
}

uint AccessPointWrapper::frequency()
{
    return _accessPoint->frequency();
}

bool AccessPointWrapper::connected()
{    
    return _connected;
}

void AccessPointWrapper::setConnected(bool connected)
{
    if (connected  != _connected)
    {
        _connected = connected;        
        emit connectedChanged();
    }
}

QString AccessPointWrapper::uni()
{
    return _accessPoint->uni();
}

NetworkManager::AccessPoint *AccessPointWrapper::AccessPoint()
{
    return _accessPoint;
}

void AccessPointWrapper::newSignalStrength(int newValue)
{
   // qDebug() << "Got new signal strength value of " << newValue << " for " << uni();
    emit signalStrengthChanged();
}

void AccessPointWrapper::newBitRate(int newValue)
{
    //qDebug() << "Got new bitrate vale of " << newValue;
    emit maxBitRateChanged();
}

Code for managing and displaying list of access points

By wrapping KDE’s Manager class and using the WirelessDevice class it was not such a big task to put it all together. Using a Q_PROPERTY to expose the list of access points for the UI and providing some Q_INVOKABLE for functions to be triggered via the UI.

wifiwrapper.h

#ifndef WIFIWRAPPER_H
#define WIFIWRAPPER_H

#include <QObject>
#include <NetworkManagerQt/Manager>
#include <NetworkManagerQt/WirelessDevice>
#include "accesspointwrapper.h"
#include <QList>

// Wrapping some wifi-specific parts via the NetworkManagerQt
// Exposing it to qml
class WifiWrapper : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QList<AccessPointWrapper*> accesspoints READ accesspoints NOTIFY accesspointsChanged)
public:
    explicit WifiWrapper(QObject *parent = nullptr);

    QList<AccessPointWrapper*> accesspoints();
    Q_INVOKABLE void connectToAccesspoint(AccessPointWrapper* ap);
    Q_INVOKABLE void createConnection(QString ssid, QString password, QString accesspointPath);
    Q_INVOKABLE void scan();

signals:
    void accesspointsChanged();
    void showPasswordPopup(QString info, QString ssid, QString apPath);
    void showInfoPopup(QString info);

public slots:
    void updateAccesspoints();
    void stateChanged(NetworkManager::Device::State newstate, NetworkManager::Device::State oldstate, NetworkManager::Device::StateChangeReason reason);

private:
    static bool signalStrengthMoreThan(AccessPointWrapper* ap1, AccessPointWrapper* ap2);

    QList<AccessPointWrapper*> _accesspoints;
    NetworkManager::WirelessDevice::Ptr _wifiDevice;
    QString _lastActiveConnectionPath = "";

};

#endif // WIFIWRAPPER_H

wifiwrapper.cpp

#include "wifiwrapper.h"
#include <NetworkManagerQt/WirelessSetting>
#include <NetworkManagerQt/Ipv4Setting>
#include <NetworkManagerQt/Utils>
#include <QUuid>
#include <QDebug>

using namespace NetworkManager;

WifiWrapper::WifiWrapper(QObject *parent) : QObject(parent)
{
    // Find and use first wifi-device
    const Device::List deviceList = networkInterfaces();
    _wifiDevice = nullptr;

    // We have to find some wireless device
    for (Device::Ptr dev : deviceList) {
        if (dev->type() == Device::Wifi) {
            _wifiDevice = qobject_cast<WirelessDevice *>(dev);
            break;
        }
    }

    // If we have a wifi device, read out nearby accesspoints and connect signals
    if (_wifiDevice)
    {
        updateAccesspoints();
        connect(_wifiDevice.data(), SIGNAL(accessPointAppeared(QString)), this, SLOT(updateAccesspoints()));
        connect(_wifiDevice.data(), SIGNAL(accessPointDisappeared(QString)), this, SLOT(updateAccesspoints()));
        connect(_wifiDevice.data(), SIGNAL(activeAccessPointChanged(QString)), this, SLOT(updateAccesspoints()));
        connect(_wifiDevice.data(), SIGNAL(stateChanged(NetworkManager::Device::State,NetworkManager::Device::State,NetworkManager::Device::StateChangeReason)),
                                this, SLOT(stateChanged(NetworkManager::Device::State,NetworkManager::Device::State,NetworkManager::Device::StateChangeReason)));
    }
}

QList<AccessPointWrapper*> WifiWrapper::accesspoints()
{
    return _accesspoints;
}

void WifiWrapper::connectToAccesspoint(AccessPointWrapper *ap)
{
    // Called from UI after long press on access point

    // First case, we are already connected.. Don't do anything
    if (ap->connected())
        return;

    Connection::List connections = _wifiDevice->availableConnections();

    for (Connection::Ptr con : connections)
    {
       //qDebug() << "Connection " << con;
       //qDebug() << "Settings: " << con->settings()->setting(Setting::Wireless);
       WirelessSetting::Ptr wirelessSetting = con->settings()->setting(Setting::Wireless).dynamicCast<WirelessSetting>();
       //qDebug()<< "BBSID for " << wirelessSetting->ssid() << " is " << wirelessSetting->bssid();

       // Do we already have a connection for this SSID? Try to change to a specific AP (bssid)
       if (ap->ssid() == wirelessSetting->ssid())
       {           
           QByteArray bssid =  macAddressFromString( ap->hardwareAddress());
           wirelessSetting->setBssid(bssid);
           qDebug() << con->settings()->toMap();
           QDBusPendingReply<void> replyUpd = con->update(con->settings()->toMap());
           replyUpd.waitForFinished();
           if (replyUpd.isValid())
           {
               QDBusPendingReply<void> reply = con->save();
               reply.waitForFinished();

               if (reply.isValid())
               {
                   qDebug() << "Updated settings successfully";
                   qDebug() << "Try to activate connection " << con->uuid();

                    QDBusPendingReply<QDBusObjectPath> activateReply = activateConnection(con->path(),_wifiDevice->uni(),ap->uni());
                    activateReply.waitForFinished();
                    if (activateReply.isValid())
                    {
                        // This goes well even though password could be wrong. Faulty password is being catched at a later time
                       qDebug() << "Activated connection";
                    }
                    else
                    {
                        qDebug() << "Failed to activate connection " << activateReply.error();
                    }
               }
               else
               {
                   qDebug() << reply.error();
               }
           }
           else
           {
               qDebug() << "replyUpd is not valid!";
               qDebug() << replyUpd.error();
           }

           return;
       }
    }

    // If we got here, we don't have a connection for this ssid, figure out which security settings we have and ask for password
    if(ap->AccessPoint()->wpaFlags() > 0 || ap->AccessPoint()->rsnFlags() > 0)
        emit showPasswordPopup("Enter password for " + ap->ssid(), ap->ssid(), ap->uni());
}

void WifiWrapper::createConnection(QString ssid, QString password, QString accessPointPath)
{
    // Called from UI after filling in password and pressing Connect button

    // Create a new connection and activate it
    qDebug() << "Create connection for " << ssid << " password: " << password;

    ConnectionSettings *settings = new ConnectionSettings(ConnectionSettings::Wireless);
    // Now we will prepare our new connection, we have to specify ID and create new UUID
    settings->setId(ssid);
    settings->setUuid(QUuid::createUuid().toString().mid(1, QUuid::createUuid().toString().length() - 2));

    // For wireless setting we have to specify SSID
    WirelessSetting::Ptr wirelessSetting = settings->setting(Setting::Wireless).dynamicCast<WirelessSetting>();
    wirelessSetting->setSsid(ssid.toUtf8());

    Ipv4Setting::Ptr ipv4Setting = settings->setting(Setting::Ipv4).dynamicCast<Ipv4Setting>();
    ipv4Setting->setMethod(Ipv4Setting::Automatic);

    // Optional password setting. Can be skipped if you do not need encryption.
    WirelessSecuritySetting::Ptr wifiSecurity = settings->setting(Setting::WirelessSecurity).dynamicCast<WirelessSecuritySetting>();
    wifiSecurity->setKeyMgmt(WirelessSecuritySetting::WpaPsk);
    wifiSecurity->setPsk(password);
    wifiSecurity->setInitialized(true);
    wirelessSetting->setSecurity("802-11-wireless-security");

    // We try to add and activate our new wireless connection
    QDBusPendingReply<QDBusObjectPath, QDBusObjectPath> reply = addAndActivateConnection(settings->toMap(), _wifiDevice->uni(), accessPointPath);

    reply.waitForFinished();

    // Check if this connection was added successfuly
    if (reply.isValid())
    {
        qDebug() << "Created new connection!";
        emit showInfoPopup("Created a new connection");
    }
    else
    {
        qDebug() << "Failed to create connection " << reply.error();
        emit showInfoPopup("Failed: " + reply.error().message());
    }
}

void WifiWrapper::scan()
{
  _wifiDevice->requestScan({});
}

void WifiWrapper::updateAccesspoints()
{
    qDebug() << "Updating accesspoints";
    const QStringList accessPointList = _wifiDevice->accessPoints();

    qDeleteAll(_accesspoints);
    _accesspoints.clear();

    // Which accesspoint is active/connected?
    AccessPoint::Ptr activeAP = _wifiDevice->activeAccessPoint();
    QString activeAP_uni = "None";    
    if (activeAP != nullptr)
    {
        activeAP_uni = activeAP->uni();
        ActiveConnection::Ptr acon = _wifiDevice->activeConnection();
        if (acon != nullptr)
        {
            _lastActiveConnectionPath = acon->connection()->path();
        }
    }
    else
    {
        //qDebug() << "No Active AP!!!!!!!";
    }

    for(QString s : accessPointList)
    {
        AccessPointWrapper *aw = new AccessPointWrapper();
        aw->init(s);
        if (s == activeAP_uni)
            aw->setConnected(true);

        _accesspoints.append(aw);
    }

    // Sort list of accesspoints according to signal strength
    std::sort(_accesspoints.begin(), _accesspoints.end(), signalStrengthMoreThan);
    emit accesspointsChanged();
}

void WifiWrapper::stateChanged(Device::State newstate, Device::State oldstate, Device::StateChangeReason reason)
{
    // When having faulty password we come to this point
    if (newstate == Device::Failed && reason == Device::NoSecretsReason)
    {
        Connection lastConnection(_lastActiveConnectionPath);
        emit showInfoPopup("Faulty password for " + lastConnection.name());
        // Try to remove it        
        lastConnection.remove();
    }
}

bool WifiWrapper::signalStrengthMoreThan(AccessPointWrapper *ap1, AccessPointWrapper *ap2)
{
    return ap1->signalStrength() > ap2->signalStrength();
}

main.cpp, hook things up

Setting up that we use the freevirtualkeyboard in the QT_IM_MODULE env variable. Also create the instace of our WifiWrapper class and setting the setContextProperty for it to be access via QML-code

main.cpp

#include <QGuiApplication>
#include <QtQml>
#include "wifiwrapper.h"

int main(int argc, char *argv[])
{
    // Load virtualkeyboard input context plugin
    qputenv("QT_IM_MODULE", QByteArray("freevirtualkeyboard"));

    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);

    WifiWrapper wifiWrapper;
    engine.rootContext()->setContextProperty("WifiWrapper", &wifiWrapper);
    engine.load(url);

    return app.exec();
}

QML code, the UI part

Since this is not a very complex application we kept the UI code in one qml-file. Usually the id: of parts in the UI will give a hint of what is happening. E g the idEnterPasswordPopup is for the Rectangle popping up when the user needs to type in a password. By using the Connections object it is easy to get a callback from the WifiWrapper when it is needed to type a password. Similar we have a Rectangle idInfoPopup for showing various information via a callback from WifiWrapper.

The InputPanel is provided with the freevirtualkeyboard and is the UI for showing the virtual keyboard.

By using a ListView for displaying the list of access points we do a template Component called idApInfo. All code for displaying a single instance of an access point is gathered here and then used by the ListView.

A simple RoundButton is used for the Scanning function.

main.qml

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import QtQuick.Layouts 1.3
import QtQuick.FreeVirtualKeyboard 1.0

Window {
    id: idWindow
    width: 1280
    height: 800
    visible: true
    color:"black"

    onActiveFocusItemChanged: print("activeFoucusItem", activeFocusItem)
    Component.onCompleted:
    {
        Qt.inputMethod.hide()
    }

    Component{
        id: idApInfo

        Rectangle{
            id: idRectangle
            width: idWindow.width
            height: 50
            color: "black"
            property string textColor: modelData.signalStrength > 55 ? "orange" : modelData.signalStrength > 31 ? "purple" : "blue"

            MouseArea{
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton | Qt.RightButton
                onClicked: {
                    if (mouse.button === Qt.RightButton) { // 'mouse' is a MouseEvent argument passed into the onClicked signal handler
                        console.log("right button clicked!")
                    } else if (mouse.button === Qt.LeftButton) {
                        console.log("left button clicked!")
                    }
                }

                onPressAndHold: {

                    console.log("Press and hold!!")
                    WifiWrapper.connectToAccesspoint(modelData)
                }
            }

            RowLayout{
                Layout.fillWidth: true
                Layout.fillHeight: true
                anchors.fill: parent

                Text{
                    id: idConnected
                    color: textColor
                    fontSizeMode: Text.Fit
                    font.pixelSize: idRectangle.height-5
                    minimumPixelSize: 5
                    Layout.minimumWidth: 100
                    Layout.preferredWidth: 100
                    Layout.preferredHeight: idRectangle.height
                    Layout.alignment: Qt.AlignHCenter
                    text: modelData.connected ? " *" : " -"
                }

                Text{
                    id: idBSSID
                    color: textColor
                    fontSizeMode: Text.Fit
                    font.pixelSize: idRectangle.height-5
                    minimumPixelSize: 5
                    Layout.minimumWidth: 200
                    Layout.preferredWidth: 250
                    Layout.preferredHeight: idRectangle.height
                    Layout.alignment: Qt.AlignLeft
                    text: modelData.hardwareAddress
                }
                Text{
                    id: idSSID
                    color: textColor
                    fontSizeMode: Text.Fit
                    font.pixelSize: idRectangle.height-10
                    minimumPixelSize: 1
                    Layout.minimumWidth: 100
                    Layout.preferredWidth: 200
                    Layout.maximumWidth: 200
                    Layout.preferredHeight: idRectangle.height
                    Layout.alignment: Qt.AlignLeft
                    text: modelData.ssid
                }

                Text{
                    id: idRate
                    color: textColor
                    fontSizeMode: Text.HorizontalFit
                    font.pixelSize: idRectangle.height-10
                    minimumPixelSize: 1
                    Layout.minimumWidth: 100
                    Layout.preferredWidth: 150
                    Layout.preferredHeight: idRectangle.height
                    Layout.alignment: Qt.AlignLeft
                    text: modelData.maxBitRate
                }

                Text{
                    id: idSignalStrength
                    color: textColor
                    fontSizeMode: Text.HorizontalFit
                    font.pixelSize: idRectangle.height-10
                    minimumPixelSize: 1
                    Layout.minimumWidth: 20
                    Layout.preferredWidth: 50
                    Layout.preferredHeight: idRectangle.height
                    Layout.minimumHeight: 20
                    Layout.alignment: Qt.AlignLeft
                    text: modelData.signalStrength
                }

                Text{
                    id: idSecurity
                    color: textColor
                    fontSizeMode: Text.HorizontalFit
                    font.pixelSize: idRectangle.height-10
                    minimumPixelSize: 1
                    Layout.minimumWidth: 50
                    Layout.preferredWidth: 200
                    Layout.preferredHeight: idRectangle.height
                    Layout.minimumHeight: 20
                    Layout.alignment: Qt.AlignLeft
                    text: modelData.security
                }
                Text{
                    id: idFrequency
                    color: textColor
                    fontSizeMode: Text.HorizontalFit
                    font.pixelSize: idRectangle.height-10
                    minimumPixelSize: 1
                    Layout.minimumWidth: 50
                    Layout.preferredWidth: 200
                    Layout.preferredHeight: idRectangle.height
                    Layout.minimumHeight: 20
                    Layout.alignment: Qt.AlignLeft
                    text: modelData.frequency
                }

            }
        }
    }

    RowLayout
    {
        id: idTitleRow
        width: 1280
        height: 50

        Text {
            Layout.fillWidth: true
            Layout.fillHeight: true
            font.pixelSize: 30
            minimumPixelSize: 1
            text: "ACTIVE BSSID         SSID         SPEED  SIGNAL SECURITY  FREQUENCY"
            color:"green"
        }
    }

    ListView
    {
        anchors.top: idTitleRow.bottom
        anchors.bottom: parent.bottom
        width: 1280        
        clip:false
        model: WifiWrapper.accesspoints
        delegate: idApInfo
    }

    RoundButton
    {
        id: idScanButton
        anchors.bottom: parent.bottom
        anchors.right: parent.right
        text: "Scan"
        radius: 20
        onClicked: WifiWrapper.scan()
    }

    Rectangle
    {
        id: idEnterPasswordPopup
        visible:false
        width: parent.width*0.7
        height: 250
        anchors.centerIn: parent
        anchors.verticalCenterOffset: -200
        color: "darkgrey"

        property string popupSSID : ""
        property string accesspointPath : ""

        Connections
        {
            target: WifiWrapper
            function onShowPasswordPopup(info, ssid, apPath)
            {
                idEnterPasswordPopup.visible= true
                idPopupTitle.text = info
                idEnterPasswordPopup.popupSSID = ssid
                idEnterPasswordPopup.accesspointPath = apPath
            }
        }

        ColumnLayout
        {
            anchors.fill: parent
            Rectangle
            {
                Layout.fillWidth: true
                Layout.preferredHeight: 30
                color: "#f7971c"
                Text{
                    id:idPopupTitle
                    anchors.fill : parent
                    text : "Enter password for "
                }
            }
            Rectangle
            {
                Layout.fillWidth: true
                Layout.preferredHeight: 100
                color: "lightgray"
                TextEdit
                {
                    id:idPasswordText
                    anchors.fill: parent
                    focus: true
                    MouseArea{
                        anchors.fill: parent
                        z:100
                        onClicked:{
                            console.log("Mousearea in popup clicked")
                            Qt.inputMethod.show()
                            Qt.inputMethod.commit()
                            idPasswordText.focus = true
                        }
                    }
                }
            }
            Rectangle
            {
                id: idButtonRow
                Layout.fillWidth: true
                Layout.preferredHeight: 50
                color: "darkgrey"

                RoundButton {
                    anchors.left: idButtonRow.left
                    Layout.preferredHeight: 45
                    text: "Cancel"
                    radius: 10
                    onClicked: {
                        idEnterPasswordPopup.visible = false
                        Qt.inputMethod.hide()
                    }
                }
                RoundButton {
                    anchors.right: idButtonRow.right
                    Layout.preferredHeight: 45
                    radius: 10
                    text: "Connect"
                    onClicked: {
                        WifiWrapper.createConnection(idEnterPasswordPopup.popupSSID, idPasswordText.text, idEnterPasswordPopup.accesspointPath)
                        idEnterPasswordPopup.visible = false
                        Qt.inputMethod.hide()
                    }
                }
            }
        }
    }

    InputPanel {
        // Showing the virtual keyboard
        id: inputPanel
        y: idWindow.height
        visible:true
        anchors.left: idWindow.left
        anchors.right: idWindow.right
        states: State {
            name: "visible"
            when: Qt.inputMethod.visible
            PropertyChanges {
                target: inputPanel
                y: idWindow.height - inputPanel.height
            }
        }
        transitions: Transition {
            from: ""
            to: "visible"
            reversible: true
            ParallelAnimation {
                NumberAnimation {
                    properties: "y"
                    duration: 150
                    easing.type: Easing.InOutQuad
                }
            }
        }
    }

    Rectangle
    {
        id: idInfoPopup
        visible: false
        width: parent.width
        height: 200
        y:200

        Connections
        {
            target: WifiWrapper
            function onShowInfoPopup(info) {
                idInfoPopup.visible = true
                idInfoText.text = info
            }
        }

        Text {
            id: idInfoText
            wrapMode: Text.Wrap
            text: "Some debug text or similar"
            anchors.fill: parent
        }
        RoundButton {
            anchors.right: parent.right
            anchors.bottom: parent.bottom
            Layout.preferredHeight: 50
            text: "OK"
            radius: 10
            onClicked: {
                idInfoPopup.visible = false
            }
        }
    }
}