V dnešním článku se podíváme na to, jak se z ESP32 development boardu připojit pomocí SSH ke vzdálenému počítači.
Pro vývoj je použito PlatformIO.
platformio.ini
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps = ewpa/LibSSH-ESP32@^4.2.0
main.cpp
Jediná externí závislost je libssh-esp32
.
#include <Arduino.h>
#include <WiFi.h>
#include <libssh_esp32.h>
#include "libssh_esp32_config.h"
#include <libssh/libssh.h>
#include <vector>
#include <string>
Budeme potřebovat SSID WiFi sítě a heslo.
const char *ssid = "ssid-vasi-wifi";
const char *password = "vase-nejtajnejsi-heslo";
Potřebujeme nastavit přihlašovací údaje pro SSH a příkaz který po přihlášení provedeme.
const int ssh_port = 22;
const char *ssh_username = "pi";
const char *ssh_password = "super-tajne-heslo";
const char *ssh_command = "ls -l";
Dále seznam IP adres serverů na které se chceme připojit.
std::vector<std::string> server_ips = {
"192.168.1.254",
};
Vytvoříme funkci pro připojení k WiFi. Na konci vypíšeme do konzole MAC adresu přístupového bodu, ke kterému jsme se připojili.
void connectToWiFi()
{
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.print("Připojeno k AP s MAC: ");
Serial.println(WiFi.BSSIDstr());
}
Ted ta nejdelší část. Funkce co vytvoří SSH spojení, provede příkaz a výsledek vypíše na standardní výstup. Funkce si po sobě uklízí.
/**
* Execute SSH Command
*
* @param ip Server IP
* @param username SSH Username
* @param password SSH Password
* @param command Command to execute
*/
void executeSSHCommand(const char *ip, const char *username, const char *password, const char *command)
{
// Create a new SSH session
ssh_session session = ssh_new();
if (session == NULL)
{
Serial.println("Failed to create SSH session");
return;
}
// Set SSH options for the session
ssh_options_set(session, SSH_OPTIONS_HOST, ip);
ssh_options_set(session, SSH_OPTIONS_USER, username);
// Connect to SSH server
int rc = ssh_connect(session);
if (rc != SSH_OK)
{
Serial.println("Failed to connect to SSH server");
return;
}
// Verify the server's identity
rc = ssh_userauth_password(session, NULL, password);
if (rc != SSH_AUTH_SUCCESS)
{
Serial.println("Failed to authenticate");
return;
}
// Create a new SSH channel
ssh_channel channel = ssh_channel_new(session);
if (channel == NULL)
{
Serial.println("Failed to create SSH channel");
return;
}
// Open a new SSH session
rc = ssh_channel_open_session(channel);
if (rc != SSH_OK)
{
Serial.println("Failed to open SSH session");
return;
}
// Execute the command
rc = ssh_channel_request_exec(channel, command);
if (rc != SSH_OK)
{
Serial.println("Failed to execute command");
return;
}
// Read the output of the command
char buffer[256];
int nbytes; // Number of bytes read
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
while (nbytes > 0)
{
if (write(1, buffer, nbytes) != nbytes) // Write to stdout
{
Serial.println("Failed to write to stdout");
return;
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
// Check if there was an error reading from the channel
if (nbytes < 0)
{
Serial.println("Failed to read from SSH channel");
return;
}
// Send EOF and close the channel
ssh_channel_send_eof(channel);
// Close the channel
ssh_channel_close(channel);
// Free the channel
ssh_channel_free(channel);
// Disconnect the session
ssh_disconnect(session);
// Free the session
ssh_free(session);
}
Teď potřebujeme funkci která provede SSH příkaz pro všechny naše servery, které jsou definované v server_ips
.
/**
* Execute SSH Command on all servers
*
* @param username SSH Username
* @param password SSH Password
* @param command Command to execute
*/
void executeSSHCommandOnAllServers(const char *username, const char *password, const char *command)
{
for (std::string ip : server_ips)
{
executeSSHCommand(ip.c_str(), username, password, command);
}
}
Teď už jen zbývá provést vše při startu ESPčka.
void setup()
{
Serial.begin(115200);
while (!Serial)
{
; // Wait for serial to be ready
}
connectToWiFi();
ssh_init();
executeSSHCommandOnAllServers(ssh_username, ssh_password, ssh_command);
}
Hlavní smyčku programu pro tento příklad necháme odpočívat.
void loop()
{
delay(1000); // Delay for 1 second
}
To je vše. Po kompilaci, nahrání kódu a spuštění ESPčka se připojí k WiFi a poté provede SSH připojení na zadané IP adresy a provede ls -l
příkaz. Ten vypíše do konzole.
main.cpp full
#include <Arduino.h>
#include <WiFi.h>
#include <libssh_esp32.h>
#include "libssh_esp32_config.h"
#include <libssh/libssh.h>
#include <vector>
#include <string>
// WiFi Credentials
const char *ssid = "ssid-vasi-wifi";
const char *password = "vase-nejtajnejsi-heslo";
// SSH Credentials
const int ssh_port = 22;
const char *ssh_username = "pi";
const char *ssh_password = "super-tajne-helso";
const char *ssh_command = "ls -l";
// Server IPs List
std::vector<std::string> server_ips = {
"192.168.1.254",
};
/**
* Connect to WiFi
*/
void connectToWiFi()
{
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.print("Připojeno k AP s MAC: ");
Serial.println(WiFi.BSSIDstr());
}
/**
* Execute SSH Command
*
* @param ip Server IP
* @param username SSH Username
* @param password SSH Password
* @param command Command to execute
*/
void executeSSHCommand(const char *ip, const char *username, const char *password, const char *command)
{
// Create a new SSH session
ssh_session session = ssh_new();
if (session == NULL)
{
Serial.println("Failed to create SSH session");
return;
}
// Set SSH options for the session
ssh_options_set(session, SSH_OPTIONS_HOST, ip);
ssh_options_set(session, SSH_OPTIONS_USER, username);
// Connect to SSH server
int rc = ssh_connect(session);
if (rc != SSH_OK)
{
Serial.println("Failed to connect to SSH server");
return;
}
// Verify the server's identity
rc = ssh_userauth_password(session, NULL, password);
if (rc != SSH_AUTH_SUCCESS)
{
Serial.println("Failed to authenticate");
return;
}
// Create a new SSH channel
ssh_channel channel = ssh_channel_new(session);
if (channel == NULL)
{
Serial.println("Failed to create SSH channel");
return;
}
// Open a new SSH session
rc = ssh_channel_open_session(channel);
if (rc != SSH_OK)
{
Serial.println("Failed to open SSH session");
return;
}
// Execute the command
rc = ssh_channel_request_exec(channel, command);
if (rc != SSH_OK)
{
Serial.println("Failed to execute command");
return;
}
// Read the output of the command
char buffer[256];
int nbytes; // Number of bytes read
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
while (nbytes > 0)
{
if (write(1, buffer, nbytes) != nbytes) // Write to stdout
{
Serial.println("Failed to write to stdout");
return;
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
// Check if there was an error reading from the channel
if (nbytes < 0)
{
Serial.println("Failed to read from SSH channel");
return;
}
// Send EOF and close the channel
ssh_channel_send_eof(channel);
// Close the channel
ssh_channel_close(channel);
// Free the channel
ssh_channel_free(channel);
// Disconnect the session
ssh_disconnect(session);
// Free the session
ssh_free(session);
}
/**
* Execute SSH Command on all servers
*
* @param username SSH Username
* @param password SSH Password
* @param command Command to execute
*/
void executeSSHCommandOnAllServers(const char *username, const char *password, const char *command)
{
for (std::string ip : server_ips)
{
executeSSHCommand(ip.c_str(), username, password, command);
}
}
void setup()
{
Serial.begin(115200);
while (!Serial)
{
; // Wait for serial to be ready
}
connectToWiFi();
ssh_init();
executeSSHCommandOnAllServers(ssh_username, ssh_password, ssh_command);
}
void loop()
{
delay(1000); // Delay for 1 second
}