# 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](images/wifi_infrastructure.png) #### 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](images/wifi_adhoc.png) ### 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](images/OSI-model.png) ### 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](#use-networkmanagerqt-code-example). ## 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](images/nmcli_scan.png) ## 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](https://api.kde.org/frameworks/networkmanager-qt/html/index.html) and build it using cmake. If you have problem building it please contact . ## 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](images/UI_main.png) If long pressing on an AP we don't have a configurated connection to. Pop up a password box. ![Enter pw](images/UI_first.png) Clicking in input area will pop up a virtual keyboard ![Typing](images/UI_enterpw.png) Displaying information ![Info](images/UI_info.png) Showing active connection ![Active](images/UI_showactive.png) ### Code snippet scan for networks After initializing we have a pointer to the wireless device ```c++ NetworkManager::WirelessDevice::Ptr _wifiDevice; ``` Simply calling requestScan with an empty map ```c++ _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** ```c++ #ifndef ACCESSPOINTWRAPPER_H #define ACCESSPOINTWRAPPER_H #include // 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** ```c++ #include "accesspointwrapper.h" #include #include 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** ```c++ #ifndef WIFIWRAPPER_H #define WIFIWRAPPER_H #include #include #include #include "accesspointwrapper.h" #include // Wrapping some wifi-specific parts via the NetworkManagerQt // Exposing it to qml class WifiWrapper : public QObject { Q_OBJECT Q_PROPERTY(QList accesspoints READ accesspoints NOTIFY accesspointsChanged) public: explicit WifiWrapper(QObject *parent = nullptr); QList 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 _accesspoints; NetworkManager::WirelessDevice::Ptr _wifiDevice; QString _lastActiveConnectionPath = ""; }; #endif // WIFIWRAPPER_H ``` **wifiwrapper.cpp** ```c++ #include "wifiwrapper.h" #include #include #include #include #include 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(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 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(); //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 replyUpd = con->update(con->settings()->toMap()); replyUpd.waitForFinished(); if (replyUpd.isValid()) { QDBusPendingReply reply = con->save(); reply.waitForFinished(); if (reply.isValid()) { qDebug() << "Updated settings successfully"; qDebug() << "Try to activate connection " << con->uuid(); QDBusPendingReply 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->setSsid(ssid.toUtf8()); Ipv4Setting::Ptr ipv4Setting = settings->setting(Setting::Ipv4).dynamicCast(); ipv4Setting->setMethod(Ipv4Setting::Automatic); // Optional password setting. Can be skipped if you do not need encryption. WirelessSecuritySetting::Ptr wifiSecurity = settings->setting(Setting::WirelessSecurity).dynamicCast(); 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 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** ```c++ #include #include #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** ```c++ 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 } } } } ``` # Further reading 1. [NetworkManager reference manual](https://developer-old.gnome.org/NetworkManager/1.12) 2. [Introduction to NetworkManager](https://opensource.com/article/22/4/networkmanager-linux) 3. [Wi-Fi overview - Physical layer](https://www.cnrood.com/en/media/solutions/Wi-Fi_Overview_of_the_802.11_Physical_Layer.pdf) 4. [Qt D-Bus](https://doc.qt.io/qt-5/qtdbus-index.html) 5. [Use wpa supplicant standalone](https://wiki.archlinux.org/title/wpa_supplicant) 6. [KDE NetworkManagerQt reference](https://api.kde.org/frameworks/networkmanager-qt/html/index.html) 7. [FreeVirtualKeyboard](https://github.com/githubuser0xFFFF/QtFreeVirtualKeyboard) 8. [Qt D-Bus chat example](https://doc.qt.io/qt-5/qtdbus-chat-example.html)