I'm using esp-wroom-32 dev kit to make a small IoT project. My application has a WiFi scanner function.
When I placed everything in one main.cpp file, everything was ok, but my code was hard to read or maintain, therefore, I decided to move the WiFi function into classes. It consists of 2 files:
- WirelessNetworkManager (for scanning networks, save into its memory and retrieved for future use)
- .h file
class WirelessNetworkManager {
private:
// Available networks that have been searched.
LinkedList<WirelessNetwork> _availableNetworks;
// Network which have been selected.
WirelessNetwork *_selectedNetwork;
// The last time when networks are scanned.
float _lastScanTime;
// Id of network which item will be retrieved previously.
int _previousPageId;
// Id of network from which item will be retrieved
int _pageId;
public:
WirelessNetworkManager();
// Get list of available networks.
LinkedList<WirelessNetwork> *getAvailableNetworks();
// Get list of wireless networks by using page id & maximum records per page.
WirelessNetwork *getAvailableNetworks(int pageId, int maxRecords, int &length);
// Get the id of page.
int getPageId() const;
// Get the selected network.
WirelessNetwork *getSelectedNetwork();
// Select wireless network by specifying its index.
void selectWirelessNetwork(int index);
// Disconnect to from the connect network (if any)
static void disconnect() ;
// Mark page to be loaded.
void markPageLoaded();
// Whether network records must be updated.
bool shouldRecordsRefreshed() const;
// Go to next network page
void goForward();
// Go to previous network page
void goBackward();
};
- .cpp file
#include <WirelessNetworkManager.h>
#include <WiFi.h>
//#region Constructor
WirelessNetworkManager::WirelessNetworkManager() {
this->_lastScanTime = -1.0f;
this->_selectedNetwork = nullptr;
this->_availableNetworks = LinkedList<WirelessNetwork>();
this->_pageId = 0;
this->_previousPageId = -1;
}
//#endregion
//#region Methods
LinkedList<WirelessNetwork> *WirelessNetworkManager::getAvailableNetworks() {
LinkedList<WirelessNetwork> *allNetworks = nullptr;
// Clear the previous loaded network.
this->_availableNetworks.clear();
this->_previousPageId = -1;
this->_pageId = 0;
// Clear the selected network.
this->_selectedNetwork = nullptr;
// WiFi.scanNetworks will return the number of networks found
int iDiscoveredNetworks = WiFi.scanNetworks();
delay(20);
// No network is discovered.
if (iDiscoveredNetworks == 0) {
return &this->_availableNetworks;
}
LinkedList<WirelessNetwork> availableNetworks;
for (int iNetworkIndex = 0; iNetworkIndex < iDiscoveredNetworks; iNetworkIndex++) {
String ssid(WiFi.SSID(iNetworkIndex));
char iSignalStrength = WiFi.RSSI(iNetworkIndex);
bool bIsProtected = WiFi.encryptionType(iNetworkIndex) != WIFI_AUTH_OPEN;
// Enlist the available networks.
WirelessNetwork availableNetwork = WirelessNetwork(ssid.c_str(), iSignalStrength, bIsProtected);
availableNetworks.add(availableNetwork);
}
this->_availableNetworks = availableNetworks;
allNetworks = &this->_availableNetworks;
return allNetworks;
}
WirelessNetwork *WirelessNetworkManager::getAvailableNetworks(int pageId, int maxRecords, int& length) {
if (this->_availableNetworks.size() < 1) {
length = 0;
return new WirelessNetwork[0];
}
// Update the page id.
this->_pageId = pageId;
// Get the records which must be skipped.
int skippedRecords = this->_pageId * maxRecords;
int endRecordId = skippedRecords + maxRecords;
if (endRecordId > this->_availableNetworks.size()) {
endRecordId = this->_availableNetworks.size();
}
length = endRecordId - skippedRecords;
WirelessNetwork availableNetworks[length];
Serial.print("Items = ");
Serial.println(this->_availableNetworks.size());
Serial.print("Skipped records = ");
Serial.println(skippedRecords);
Serial.print("End records = ");
Serial.println(endRecordId);
int index = 0;
for (int j = skippedRecords; j < endRecordId; j++) {
Serial.print("Id ");
Serial.println(j);
char szItemTitle[128] = "";
char szValue[10] = "";
char szNetworkName[128] = "";
const bool bIsProtected = this->_availableNetworks.get(j).getProtected();
strcat(szItemTitle, " ");
strcat(szNetworkName, this->_availableNetworks.get(j).getName());
strcat(szItemTitle, szNetworkName);
strcat(szItemTitle, !bIsProtected ? " " : "*");
Serial.println(szItemTitle);
WirelessNetwork availableNetwork(szNetworkName,
this->_availableNetworks.get(j).getStrength(), this->_availableNetworks.get(j).getProtected());
availableNetworks[index] = availableNetwork;
// *(availableNetworks + index) = availableNetwork;
index++;
}
length = 0;
return new WirelessNetwork[0];
}
int WirelessNetworkManager::getPageId() const {
return this->_pageId;
}
WirelessNetwork *WirelessNetworkManager::getSelectedNetwork() {
return this->_selectedNetwork;
}
void WirelessNetworkManager::selectWirelessNetwork(int index) {
if (index < 0 || index > this->_availableNetworks.size() - 1) {
return;
}
WirelessNetwork selectedNetwork = this->_availableNetworks.get(index);
this->_selectedNetwork = &selectedNetwork;
}
void WirelessNetworkManager::disconnect() {
WiFi.disconnect();
}
void WirelessNetworkManager::markPageLoaded() {
this->_previousPageId = this->_pageId;
}
bool WirelessNetworkManager::shouldRecordsRefreshed() const {
return this->_pageId != this->_previousPageId;
}
void WirelessNetworkManager::goForward() {
// TODO: Check if page can be gone forward or not.
this->_pageId++;
}
void WirelessNetworkManager::goBackward() {
// TODO: Check if page can be gone backward or not.
if (this->_pageId < 2) {
return;
}
this->_pageId--;
}
//#endregion
- WirelessNetwork (define wireless network as entities)
.h file
class WirelessNetwork {
private:
// Name of network.
char m_szName[128];
// Signal strength.
char m_iSignalStrength;
// Whether wifi is protected or not.
bool m_bIsProtected;
public:
WirelessNetwork();
// Constructor
WirelessNetwork(const char *name, char signalStrength, bool isProtected);
WirelessNetwork(WirelessNetwork const &wirelessNetwork);
// Get network name.
const char *getName();
// Get network strength.
char getStrength();
// Whether wifi is protected or not.
bool getProtected();
};
.cpp file
#include "WirelessNetwork.h"
#include <cstring>
//#region Constructor
WirelessNetwork::WirelessNetwork() {
}
WirelessNetwork::WirelessNetwork(const char *name, char signalStrength, bool bIsProtected) {
strcpy(this->m_szName, name);
this->m_iSignalStrength = signalStrength;
this->m_bIsProtected = bIsProtected;
}
WirelessNetwork::WirelessNetwork(WirelessNetwork const &wirelessNetwork) {
strcpy(this->m_szName, wirelessNetwork.m_szName);
this->m_iSignalStrength = wirelessNetwork.m_iSignalStrength;
this->m_bIsProtected = wirelessNetwork.m_bIsProtected;
}
//#endregion
//#region Methods
const char *WirelessNetwork::getName() {
char* szName = this->m_szName;
return szName;
}
char WirelessNetwork::getStrength() {
return this->m_iSignalStrength;
}
bool WirelessNetwork::getProtected() {
return this->m_bIsProtected;
}
//#endregion
This is how I use them in main.cpp file:
#include <Wire.h> //Including wire library
#include "WiFi.h"
#include "WirelessNetwork.h"
#include <LinkedList.h>
#include <HTTPClient.h>
#include <WirelessNetworkManager.h>
#define NETWORK_VIEW_ID 1
#define NETWORK_STATUS_LOADED 1
#define NETWORK_STATUS_LOADING 2
// Instance for managing wireless connection.
WirelessNetworkManager *m_WirelessNetworkManager = new WirelessNetworkManager();
int m_iMaxNetworkPageItems = 4;
bool m_bMustLoadWifiNetworks = true;
int m_iActivatedScreen = NETWORK_VIEW_ID;
int m_NetworkProcessStatus = NETWORK_STATUS_LOADING;
void setup() {
// Open serial connection.
Serial.begin(115000);
// Set WiFi to station mode and disconnect from an AP if it was previously connected
WiFi.mode(WIFI_STA);
WiFi.disconnect();
}
void loop() {
String message;
if (m_iActivatedScreen != NETWORK_VIEW_ID) {
return;
}
// Networks must be loaded.
if (m_bMustLoadWifiNetworks) {
m_bMustLoadWifiNetworks = false;
m_NetworkProcessStatus = NETWORK_STATUS_LOADING;
// Scan for available networks.
LinkedList <WirelessNetwork> *availableNetworks = m_WirelessNetworkManager->getAvailableNetworks();
// No network is discovered.
if (availableNetworks->size() < 1) {
return;
}
for (int i = 0; i < availableNetworks->size(); i++) {
char szItemTitle[128] = "";
char szValue[10] = "";
char szNetworkName[128] = "";
const bool bIsProtected = availableNetworks->get(i).getProtected();
strcat(szItemTitle, " ");
strcat(szNetworkName, availableNetworks->get(i).getName());
strcat(szItemTitle, szNetworkName);
strcat(szItemTitle, !bIsProtected ? " " : "*");
// Log the networks onto terminal
Serial.println(szItemTitle);
}
m_NetworkProcessStatus = NETWORK_STATUS_LOADED;
}
// Wifi already loaded.
if (m_NetworkProcessStatus == NETWORK_STATUS_LOADED && m_WirelessNetworkManager->shouldRecordsRefreshed()) {
int pageId = m_WirelessNetworkManager->getPageId();
// Get the available networks which have been paginated.
int length = 0;
WirelessNetwork *paginatedNetworks = m_WirelessNetworkManager->getAvailableNetworks(pageId,
m_iMaxNetworkPageItems,
length);
Serial.print("Length = ");
Serial.println(length);
for (int iNetworkIndex = 0; iNetworkIndex < length; iNetworkIndex++) {
// Get the available network.
WirelessNetwork availableNetwork = *(paginatedNetworks + iNetworkIndex);
char szItemTitle[128] = "";
char szValue[10] = "";
char szNetworkName[128] = "";
const bool bIsProtected = availableNetwork.getProtected();
strcat(szItemTitle, " ");
strcat(szNetworkName, availableNetwork.getName());
strcat(szItemTitle, szNetworkName);
strcat(szItemTitle, !bIsProtected ? " " : "*");
delay(20);
}
m_WirelessNetworkManager->markPageLoaded();
}
}
This is what happening:
- When I use
m_WirelessNetworkManager->getAvailableNetworks()to scan for available networks and display them on the screen, the networks are displayed correctly, which are:
- WiFI 01
- Wifi 02
- Wifi 03
- Wifi 04
- Those wifi networks are saved automatically into
WirelessNetworkManager - Then I used
WirelessNetwork *paginatedNetworks = m_WirelessNetworkManager->getAvailableNetworks(pageId, m_iMaxNetworkPageItems,length)to display records with pagination info, the list was displayed as:
- WiFI 01
- *&(&(*&( (Strange characters)
- &*^&*^*& (Strange characters)
It is very weird, I'm still debugging this issue, one thing I'm thinking about is char* pointer, I think they are pointing to dangling pointer, but don't know whether what I'm thinking is correct or not.
Are there any ways to handle string correctly in C++ ? As C#, string is primitive, can we do that in C++ ?
Thank you,