generated from Projetos/RaspberryPi_app
286 lines
9.3 KiB
C++
286 lines
9.3 KiB
C++
// main.cpp — Info display for SSD1306 128x64 using gfx::LinuxFBDisplay.
|
|
//
|
|
// Build: see CMakeLists.txt
|
|
// Run: ./gfx_info_display # defaults to /dev/fb1
|
|
// ./gfx_info_display /dev/fb0 # override
|
|
|
|
#include "gfx_canvas.h"
|
|
#include "gfx_font_5x7.h"
|
|
#include "linux_fb_display.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <ctime>
|
|
#include <string>
|
|
|
|
#include <ifaddrs.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <net/if.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
|
|
#include <netlink/netlink.h>
|
|
#include <netlink/genl/genl.h>
|
|
#include <netlink/genl/ctrl.h>
|
|
#include <linux/nl80211.h>
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Configuration
|
|
// ---------------------------------------------------------------------------
|
|
constexpr int16_t SCREEN_W = 128;
|
|
constexpr int16_t SCREEN_H = 64; // 16 yellow on top and 48 blue on bottom
|
|
|
|
constexpr const char* ETH_IFACE = "eth0";
|
|
constexpr const char* WLAN_IFACE = "wlan0";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Network helper
|
|
// ---------------------------------------------------------------------------
|
|
static std::string get_ip(const char* iface_name) {
|
|
struct ifaddrs* ifaddr = nullptr;
|
|
if (getifaddrs(&ifaddr) == -1) return "err";
|
|
|
|
std::string result = "down";
|
|
for (auto* ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
|
|
if (!ifa->ifa_addr) continue;
|
|
if (ifa->ifa_addr->sa_family != AF_INET) continue;
|
|
if (std::strcmp(ifa->ifa_name, iface_name) != 0) continue;
|
|
|
|
char buf[INET_ADDRSTRLEN];
|
|
auto* sa = reinterpret_cast<sockaddr_in*>(ifa->ifa_addr);
|
|
inet_ntop(AF_INET, &sa->sin_addr, buf, sizeof(buf));
|
|
result = buf;
|
|
break;
|
|
}
|
|
|
|
freeifaddrs(ifaddr);
|
|
return result;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Wi-Fi helper — SSID and RSSI via nl80211 / libnl-genl
|
|
// ---------------------------------------------------------------------------
|
|
struct WifiScanResult {
|
|
char ssid[33];
|
|
uint8_t bssid[6];
|
|
bool have_ssid = false;
|
|
bool have_bssid = false;
|
|
};
|
|
|
|
struct WifiStationResult {
|
|
int rssi = 0;
|
|
bool have_rssi = false;
|
|
};
|
|
|
|
static int scan_handler(struct nl_msg* msg, void* arg) {
|
|
auto* result = static_cast<WifiScanResult*>(arg);
|
|
auto* gnlh = static_cast<genlmsghdr*>(nlmsg_data(nlmsg_hdr(msg)));
|
|
|
|
struct nlattr* tb[NL80211_ATTR_MAX + 1];
|
|
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
|
genlmsg_attrlen(gnlh, 0), nullptr);
|
|
|
|
if (!tb[NL80211_ATTR_BSS]) return NL_SKIP;
|
|
|
|
struct nlattr* bss[NL80211_BSS_MAX + 1];
|
|
nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], nullptr);
|
|
|
|
// Only process the BSS we are associated with.
|
|
if (!bss[NL80211_BSS_STATUS]) return NL_SKIP;
|
|
if (nla_get_u32(bss[NL80211_BSS_STATUS]) != NL80211_BSS_STATUS_ASSOCIATED)
|
|
return NL_SKIP;
|
|
|
|
if (bss[NL80211_BSS_BSSID]) {
|
|
std::memcpy(result->bssid, nla_data(bss[NL80211_BSS_BSSID]), 6);
|
|
result->have_bssid = true;
|
|
}
|
|
|
|
// Walk Information Elements to find SSID (IE id 0).
|
|
if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
|
|
const auto* ie = static_cast<const uint8_t*>(
|
|
nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]));
|
|
int ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
|
|
while (ie_len >= 2) {
|
|
if (ie[0] == 0 && ie[1] > 0 && ie[1] <= 32) {
|
|
std::memcpy(result->ssid, ie + 2, ie[1]);
|
|
result->ssid[ie[1]] = '\0';
|
|
result->have_ssid = true;
|
|
break;
|
|
}
|
|
ie_len -= 2 + ie[1];
|
|
ie += 2 + ie[1];
|
|
}
|
|
}
|
|
|
|
return NL_SKIP;
|
|
}
|
|
|
|
static int station_handler(struct nl_msg* msg, void* arg) {
|
|
auto* result = static_cast<WifiStationResult*>(arg);
|
|
auto* gnlh = static_cast<genlmsghdr*>(nlmsg_data(nlmsg_hdr(msg)));
|
|
|
|
struct nlattr* tb[NL80211_ATTR_MAX + 1];
|
|
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
|
genlmsg_attrlen(gnlh, 0), nullptr);
|
|
|
|
if (!tb[NL80211_ATTR_STA_INFO]) return NL_SKIP;
|
|
|
|
struct nlattr* sta[NL80211_STA_INFO_MAX + 1];
|
|
nla_parse_nested(sta, NL80211_STA_INFO_MAX,
|
|
tb[NL80211_ATTR_STA_INFO], nullptr);
|
|
|
|
if (sta[NL80211_STA_INFO_SIGNAL]) {
|
|
result->rssi = static_cast<int8_t>(
|
|
nla_get_u8(sta[NL80211_STA_INFO_SIGNAL]));
|
|
result->have_rssi = true;
|
|
}
|
|
|
|
return NL_SKIP;
|
|
}
|
|
|
|
static bool get_wifi_info(const char* iface, char* ssid_out, int* rssi_out) {
|
|
struct nl_sock* sock = nl_socket_alloc();
|
|
if (!sock) return false;
|
|
|
|
if (genl_connect(sock) < 0) { nl_socket_free(sock); return false; }
|
|
|
|
int nl80211_id = genl_ctrl_resolve(sock, "nl80211");
|
|
if (nl80211_id < 0) { nl_socket_free(sock); return false; }
|
|
|
|
unsigned int if_index = if_nametoindex(iface);
|
|
if (!if_index) { nl_socket_free(sock); return false; }
|
|
|
|
// --- GET_SCAN dump → find associated BSS, extract SSID ---
|
|
WifiScanResult scan{};
|
|
nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, scan_handler, &scan);
|
|
|
|
struct nl_msg* msg = nlmsg_alloc();
|
|
genlmsg_put(msg, 0, 0, nl80211_id, 0, NLM_F_DUMP, NL80211_CMD_GET_SCAN, 0);
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
|
|
nl_send_auto(sock, msg);
|
|
nlmsg_free(msg);
|
|
nl_recvmsgs_default(sock);
|
|
|
|
if (!scan.have_ssid) { nl_socket_free(sock); return false; }
|
|
std::strncpy(ssid_out, scan.ssid, 32);
|
|
ssid_out[32] = '\0';
|
|
|
|
// --- GET_STATION for the AP → extract RSSI ---
|
|
WifiStationResult station{};
|
|
nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, station_handler, &station);
|
|
|
|
msg = nlmsg_alloc();
|
|
genlmsg_put(msg, 0, 0, nl80211_id, 0, 0, NL80211_CMD_GET_STATION, 0);
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
|
|
nla_put(msg, NL80211_ATTR_MAC, 6, scan.bssid);
|
|
nl_send_auto(sock, msg);
|
|
nlmsg_free(msg);
|
|
nl_recvmsgs_default(sock);
|
|
|
|
if (station.have_rssi) *rssi_out = station.rssi;
|
|
|
|
nl_socket_free(sock);
|
|
return true;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Text centering helper
|
|
// ---------------------------------------------------------------------------
|
|
static void draw_centered(gfx::GfxCanvas& disp, int16_t y, const char* text) {
|
|
int16_t bx, by;
|
|
uint16_t bw, bh;
|
|
disp.getTextBounds(text, 0, 0, bx, by, bw, bh);
|
|
disp.drawString((SCREEN_W - static_cast<int16_t>(bw)) / 2, y, text,
|
|
gfx::colors::white);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Signal handling
|
|
// ---------------------------------------------------------------------------
|
|
static volatile sig_atomic_t g_running = 1;
|
|
|
|
static void signal_handler(int) { g_running = 0; }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Main
|
|
// ---------------------------------------------------------------------------
|
|
int main(int argc, char* argv[]) {
|
|
const char* fb_device = "/dev/fb1";
|
|
if (argc > 1) fb_device = argv[1];
|
|
|
|
gfx::LinuxFBDisplay display(SCREEN_W, SCREEN_H);
|
|
if (!display.open(fb_device)) return EXIT_FAILURE;
|
|
|
|
display.setFont(gfx::font_5x7);
|
|
display.setTextColor(gfx::colors::white);
|
|
display.setTextWrap(false);
|
|
|
|
signal(SIGINT, signal_handler);
|
|
signal(SIGTERM, signal_handler);
|
|
|
|
char line_buf[64];
|
|
|
|
while (g_running) {
|
|
display.fillScreen(gfx::colors::black);
|
|
|
|
// --- Date and time ---
|
|
std::time_t now = std::time(nullptr);
|
|
std::tm* tm = std::localtime(&now);
|
|
|
|
std::snprintf(line_buf, sizeof(line_buf), "%04d-%02d-%02d %02d:%02d:%02d",
|
|
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
|
//draw_centered(display, 0, line_buf);
|
|
display.setCursor(0, 0);
|
|
display.print(line_buf);
|
|
|
|
// --- Separator ---
|
|
// display.drawHLine(0, 16, SCREEN_W, gfx::colors::white);
|
|
|
|
// --- Hostname ---
|
|
gethostname(line_buf, sizeof(line_buf) - 1);
|
|
display.setCursor(0, 8);
|
|
display.print(line_buf);
|
|
|
|
// --- Network info ---
|
|
std::string eth_ip = get_ip(ETH_IFACE);
|
|
std::string wlan_ip = get_ip(WLAN_IFACE);
|
|
|
|
std::snprintf(line_buf, sizeof(line_buf), "eth0: %s", eth_ip.c_str());
|
|
display.setCursor(0, 16);
|
|
display.print(line_buf);
|
|
|
|
std::snprintf(line_buf, sizeof(line_buf), "wlan0: %s", wlan_ip.c_str());
|
|
display.setCursor(0, 24);
|
|
display.print(line_buf);
|
|
|
|
// --- Wi-Fi SSID / RSSI ---
|
|
char ssid[33] = {};
|
|
int rssi = 0;
|
|
if (get_wifi_info(WLAN_IFACE, ssid, &rssi)) {
|
|
std::snprintf(line_buf, sizeof(line_buf), "SSID: %s", ssid);
|
|
display.setCursor(0, 32);
|
|
display.print(line_buf);
|
|
|
|
std::snprintf(line_buf, sizeof(line_buf), "RSSI: %d dBm", rssi);
|
|
display.setCursor(0, 40);
|
|
display.print(line_buf);
|
|
}
|
|
|
|
display.update();
|
|
|
|
// --- Sleep aligned to next whole second ---
|
|
struct timespec ts{};
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
ts.tv_sec += 1;
|
|
ts.tv_nsec = 0;
|
|
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, nullptr);
|
|
}
|
|
|
|
display.fillScreen(gfx::colors::black);
|
|
std::printf("Exiting.\n");
|
|
return EXIT_SUCCESS;
|
|
}
|