STM32 invia email con allegati e SSL (come Gmail): w5500, enc28j60, SD e SPI Flash – 4
Finalmente un aggiornamento per la mia libreria EMailSender. La versione precedente (v2.x) poteva inviare email con ethernet utilizzando servizi senza SSL come SendGrid (“Invia email con allegati (libreria v2.x): Arduino Ethernet“) dopo un grande lavoro e studio ora posso inviare email con dispositivi ethernet standard come w5500 o enc28j60 con supporto SSL, e possiamo usare Gmail o altri fornitori di servizi SSL.
Ma abbiamo bisogno di dispositivi come STM32 e ESP32, perché abbiamo bisogno di più di 64Kb su flash e una buona quantità di SRAM.
Questa libreria utilizza l’SMTP:
Il Simple Mail Transfer Protocol (SMTP) è un protocollo di comunicazione per la trasmissione di posta elettronica. Come standard Internet, SMTP è stato definito per la prima volta nel 1982 da RFC 821, e aggiornato nel 2008 da RFC 5321 per aggiunte di SMTP Esteso, che è la varietà di protocollo ampiamente utilizzata oggi. I server di posta e altri agenti di trasferimento dei messaggi utilizzano SMTP per inviare e ricevere messaggi di posta. Sistemi proprietari come Microsoft Exchange e IBM Notes e sistemi di posta elettronica web come Outlook.com, Gmail e Yahoo! Mail possono utilizzare protocolli non standard internamente, ma tutti utilizzano SMTP quando inviano o ricevono email da fuori i loro sistemi. I server SMTP comunemente utilizzano il Transmission Control Protocol sulla porta numero 25. (cit WiKi)
Ad esempio, uso un account Gmail dedicato; creo un nuovo account perché devo creare delle password applicative.
Libreria
Puoi ottenere la libreria direttamente da Arduino Library Manager.
O puoi scaricarla da GitHub
Supporto Ethernet e SSL
Prima di tutto ricorda di leggere l’articolo “STM32: ethernet w5500 standard (HTTP) e SSL (HTTPS)” per una migliore comprensione.
Esiste una vasta selezione di librerie, ma quella standard è la scelta migliore. Puoi trovarla nel gestore librerie Arduino standard.
Se usi enc28j60 puoi utilizzare EthernetENC pronto per essere utilizzato con SSLClient, leggi l’articolo “STM32: ethernet enc28j60 standard (HTTP) e SSL (HTTPS)“.
SSLClient
Questa libreria e dispositivo non supportano SSL, quindi esiste una libreria alternativa chiamata SSLClient che necessita di una piccola patch di Ethernet.
SSLClient aggiunge la funzionalità TLS 1.2 a qualsiasi libreria di rete che implementa l’interfaccia Arduino Client, inclusi le classi Arduino EthernetClient e WiFiClient. SSLClient è stato creato per integrare TLS in modo trasparente con l’infrastruttura Arduino utilizzando BearSSL come motore TLS sottostante. A differenza di ArduinoBearSSL, SSLClient è completamente autonomo e non richiede alcuna hardware aggiuntivo (oltre a una connessione di rete). (cit.)
SSLClient con Ethernet
Se stai utilizzando la libreria Ethernet Arduino, dovrai modificare la libreria per supportare le dimensioni dei buffer grandi richieste da SSL (dettagliato nelle risorse). Puoi modificare tu stesso la libreria o utilizzare questo fork della libreria Ethernet con la modifica. Per utilizzare il fork: scarica una copia compressa del fork tramite GitHub, usa il pulsante “aggiungi una libreria .zip” in Arduino per installare la libreria e sostituisci #include "Ethernet.h"
con #include "EthernetLarge.h"
nel tuo sketch. In alternativa, se per qualche motivo questa soluzione non funziona, puoi applicare la modifica manualmente seguendo le istruzioni qui sotto.
Estensione del buffer
Ho notato anche che per ottenere una buona stabilità probabilmente devi cambiare qualcosa.
In SSLClient.h devi cambiare questa riga.
unsigned char m_iobuf[2048];
in
unsigned char m_iobuf[BR_SSL_BUFSIZE_BIDI];
Modifica Manuale
Prima, trova la posizione della libreria nella directory dove è installato Arduino (C:\Program Files (x86)\Arduino
su Windows). All’interno di questa directory, naviga fino a libraries\Ethernet\src
(C:\Program Files (x86)\Arduino\libraries\Ethernet\src
su Windows). Modifica Ethernet.h
per sostituire queste righe:
...
// Configura il numero massimo di socket da supportare. I chip W5100 possono avere
// fino a 4 socket. W5200 & W5500 possono avere fino a 8 socket. Diversi byte
// di RAM vengono utilizzati per ogni socket. Ridurre il massimo può risparmiare RAM, ma
// sei limitato a meno connessioni simultanee.
#if defined(RAMEND) && defined(RAMSTART) && ((RAMEND - RAMSTART) <= 2048)
#define MAX_SOCK_NUM 4
#else
#define MAX_SOCK_NUM 8
#endif
// Per impostazione predefinita, ogni socket utilizza buffer da 2K all'interno del chip Wiznet. Se
// MAX_SOCK_NUM è impostato su meno del massimo del chip, decommentando
// questo verranno utilizzati buffer più grandi all'interno del chip Wiznet. Buffer grandi
// possono davvero aiutare con protocolli UDP come Artnet. In teoria buffer più grandi
// dovrebbero permettere un TCP più veloce su collegamenti ad alta latenza, ma questo
// non sembra sempre funzionare nella pratica (forse bug di Wiznet?)
//#define ETHERNET_LARGE_BUFFERS
...
Con queste:
...
// Configura il numero massimo di socket da supportare. I chip W5100 possono avere
// fino a 4 socket. W5200 & W5500 possono avere fino a 8 socket. Diversi byte
// di RAM vengono utilizzati per ogni socket. Ridurre il massimo può risparmiare RAM, ma
// sei limitato a meno connessioni simultanee.
#define MAX_SOCK_NUM 2
// Per impostazione predefinita, ogni socket utilizza buffer da 2K all'interno del chip Wiznet. Se
// MAX_SOCK_NUM è impostato su meno del massimo del chip, decommentando
// questo verranno utilizzati buffer più grandi all'interno del chip Wiznet. Buffer grandi
// possono davvero aiutare con protocolli UDP come Artnet. In teoria buffer più grandi
// dovrebbero permettere un TCP più veloce su collegamenti ad alta latenza, ma questo
// non sembra sempre funzionare nella pratica (forse bug di Wiznet?)
#define ETHERNET_LARGE_BUFFERS
...
Potresti aver bisogno di utilizzare sudo
o permessi di amministratore per fare questa modifica. Cambiamo MAX_SOCK_NUM
e ETHERNET_LARGE_BUFFERS
in modo che l’hardware Ethernet possa allocare uno spazio più grande per SSLClient. Tuttavia, uno svantaggio di questa modifica è che possiamo avere solo due socket contemporaneamente. Poiché la maggior parte dei microprocessori ha a malapena abbastanza memoria per una connessione SSL, questa limitazione sarà raramente incontrata nella pratica.
Recupero certificato
Nel file predefinito ho già caricato il certificato Gmail, ma se vuoi utilizzare un altro fornitore SSL devi recuperare il relativo certificato.
Per utilizzare un SSL, abbiamo bisogno del certificato del server, ma in questo caso, SSLClient utilizza un trucco dato dall’implementazione BearSSL. Questo motore di verifica minimale x509 consente l’uso di Trust Anchors.
Ho aggiunto un semplice generatore online che puoi trovare qui.
Devi solo scrivere l’indirizzo del sito (httpbin.org) nella prima casella di input, fare clic su Genera codice
, copiare il codice e inserirlo all’interno di un file chiamato trust_anchors.h
.
Costruttore
Il valore predefinito è abbastanza semplice e utilizza Gmail come server SMTP.
EMailSender emailSend("smtp.account@gmail.com", "password");
Se vuoi utilizzare un altro fornitore, puoi utilizzare un costruttore più complesso (ma semplice)
EMailSender(const char* email_login, const char* email_password, const char* email_from, const char* name_from, const char* smtp_server, uint16_t smtp_port );
EMailSender(const char* email_login, const char* email_password, const char* email_from, const char* smtp_server, uint16_t smtp_port);
EMailSender(const char* email_login, const char* email_password, const char* email_from, const char* name_from );
EMailSender(const char* email_login, const char* email_password, const char* email_from);
EMailSender(const char* email_login, const char* email_password);
email_login
: login all’account SMTP- email_password: password account SMTP
- email_from: email del mittente
- name_from: il nome utente del mittente
smtp_server
: server SMTPsmtp_port
: porta SMTP
Puoi anche gestire i parametri in tempo reale con questi comandi:
void setSMTPPort(uint16_t smtp_port);
void setSMTPServer(const char* smtp_server);
void setEMailLogin(const char* email_login);
void setEMailFrom(const char* email_from);
void setNameFrom(const char* name_from);
void setEMailPassword(const char* email_password);
Un altro parametro che puoi impostare dopo il costruttore è
void setUseAuth(bool useAuth = true);
void setPublicIpDescriptor(const char *publicIpDescriptor = "mischianti");
Il primo permette di escludere la fase di autenticazione, il secondo cambia l’identificazione del messaggio HELO.
Le altre due configurazioni aggiuntive sono
void setEHLOCommand(bool useEHLO = false)
void setSASLLogin(bool isSASLLogin = false)
Il primo cambia il comando HELO in EHLO, necessario per i server SMTP Postfix.
Il secondo attiva il login SASL, quindi il login e la password vengono inviati tutti in una riga.
Devi connetterti al WIFI :P.
Invia email
Crea un messaggio con la struttura EMailMessage
EMailSender::EMailMessage message;
message.subject = "Oggetto";
message.message = "Ciao, come stai<br>Io bene.";
Invia messaggio:
EMailSender::Response resp = emailSend.send("account_to_send@gmail.com", message);
Poi controlla la risposta:
Serial.println("Stato invio: ");
Serial.println(resp.code);
Serial.println(resp.desc);
Serial.println(resp.status);
Esempio di output:
Connessione: STABILITA
Indirizzo IP ottenuto: 192.168.1.104
Stato invio:
1
0
Messaggio inviato!
Puoi inviare a una lista di email e selezionare un CC o CCn; per esempio, inviare a 3 email
const char* arrayOfEmail[] = {"<PRIMO>@gmail.com", "<SECONDO>@yahoo.com", "<TERZO>@hotmail.com"};
EMailSender::Response resp = emailSend.send(arrayOfEmail, 3, message);
o a 3 email, la prima come A e l’ultima come CC
const char* arrayOfEmail[] = {"<PRIMO>@gmail.com", "<SECONDO>@yahoo.com", "<TERZO>@hotmail.com"};
EMailSender::Response resp = emailSend.send(arrayOfEmail, 1, 2, message);
o il primo come A, il secondo come CC e il terzo come CCn
const char* arrayOfEmail[] = {"<PRIMO>@gmail.com", "<SECONDO>@yahoo.com", "<TERZO>@hotmail.com"};
EMailSender::Response resp = emailSend.send(arrayOfEmail, 1,1,1, message);
Gestione allegati
Questa libreria ti permette di inviare allegati, ora ti mostro solo il codice base, ma dopo, dobbiamo esaminare meglio la configurazione dei dispositivi di archiviazione.
byte attachsNumber = 2;
EMailSender::FileDescriptior fileDescriptor[attachsNumber];
fileDescriptor[0].filename = F("test.txt");
fileDescriptor[0].url = F("/test.txt");
fileDescriptor[0].mime = MIME_TEXT_HTML;
fileDescriptor[0].storageType = EMailSender::EMAIL_STORAGE_TYPE_SD;
fileDescriptor[1].filename = F("logo.jpg");
fileDescriptor[1].url = F("/logo.jpg");
fileDescriptor[1].mime = "image/jpg";
fileDescriptor[1].encode64 = true;
fileDescriptor[1].storageType = EMailSender::EMAIL_STORAGE_TYPE_SD;
EMailSender::Attachments attachs = {attachsNumber, fileDescriptor};
Fondamentalmente, FileDescriptior
(sì, c’è un errore ma per la compatibilità all’indietro, non posso correggerlo), è la struttura che identifica il file da caricare.
typedef struct {
StorageType storageType = EMAIL_STORAGE_TYPE_SD;
String mime;
bool encode64 = false;
String filename;
String url;
} FileDescriptior;
Ha bisogno del storageType
, del tipo mime se il file deve essere codificato, del nome del file e dell’URL dove si trova il file.
Devi generare un array con il numero di file che desideri e aggiungere la struttura Attachments
alla fine del comando send
.
Invia email con Gmail, STM32F4 e w5500
Controlla il paragrafo su come creare una password per l’applicazione (alla fine dell’articolo).
La prima cosa da fare è la configurazione della libreria, dobbiamo impostare il tipo di rete corretto e attivare SSL per il client.
Configurazione della libreria
Devi aprire il file EMailSenderKey.h
e fare questa modifica, nella sezione STM32:
#ifndef DEFAULT_EMAIL_NETWORK_TYPE_STM32
#define DEFAULT_EMAIL_NETWORK_TYPE_STM32 NETWORK_W5100
#define DEFAULT_INTERNAL_STM32_STORAGE STORAGE_NONE
#define DEFAULT_EXTERNAL_STM32_STORAGE STORAGE_NONE
#endif
Adesso non abbiamo bisogno di alcuna archiviazione, non inviamo allegati.
E per attivare SSL è importante decommentare la definizione SSLCLIENT_WRAPPER
.
#define SSLCLIENT_WRAPPER
STM32F4 Black-pill
Per questo test useremo uno STM32F4 perché, come già descritto, abbiamo bisogno di più di 64Kb di flash per gestire una connessione SSL, se hai un dispositivo con meno potenza puoi seguire il tutorial per dispositivi Arduino con SendGrid “Invia email con allegati (libreria v2.x): Arduino Ethernet”.
Qui la mia selezione di dispositivi STM32 STM32F103C8T6 STM32F401 STM32F411 ST-Link v2 ST-Link v2 official
Collegamento ethernet w5500
Andremo ad attaccare il w5500 al SPI primario con SS su PA3.
Qui i miei dispositivi ethernet testati w5500 lite - w5500 - enc26j60 mini - enc26j60 - lan8720
STM32 | w5500 SPI1 |
---|---|
PA3 | CS |
PA5 | SCK |
PA6 | MISO |
PA7 | MOSI |
3.3v | 3.3v |
GND | GND |
Puoi ottenere tutte le informazioni su cablaggio e codice nell’articolo relativo “STM32: ethernet w5500 con plain (HTTP) e SSL (HTTPS)”.
Sketch di esempio
Ecco l’esempio completo di sketch.
/**
* EMailSender ^3.0.0 on STM32
*
* Send Email with GMail (application password see the article on how to configure)
*
* Need FLASH > 64K if you want use GMail with SSL
* alternatively you can use SendGrid without activation
* of SSLClient
*
* and ethernet w5500 SS
*
To activate SSL you must uncomment
#define SSLCLIENT_WRAPPER
#ifndef DEFAULT_EMAIL_NETWORK_TYPE_STM32
#define DEFAULT_EMAIL_NETWORK_TYPE_STM32 NETWORK_W5100
#define DEFAULT_INTERNAL_STM32_STORAGE STORAGE_NONE
#define DEFAULT_EXTERNAL_STM32_STORAGE STORAGE_NONE
#endif
*
* @author Renzo Mischianti <www.mischianti.org>
* @details www.mischianti.org
* @version 0.1
* @date 2022-03-22
*
* @copyright Copyright (c) 2022
*
*/
#include <Arduino.h>
#include <SPI.h>
#include <EthernetLarge.h>
#include "EMailSender.h"
#define MACADDRESS 0x00,0x01,0x02,0x03,0x04,0x05
#define MYIPADDR 192,168,1,28
#define MYIPMASK 255,255,255,0
#define MYDNS 192,168,1,1
#define MYGW 192,168,1,1
uint8_t macaddress[6] = {MACADDRESS};
EMailSender emailSend("account@gmail.com", "<APPLICATION PASSWORD>");
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(100);
}
Serial.println("Begin Ethernet");
Ethernet.init(PA3);
if (Ethernet.begin(macaddress)) { // Dynamic IP setup
Serial.println("DHCP OK!");
}else{
Serial.println("Failed to configure Ethernet using DHCP");
// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
}
IPAddress ip(MYIPADDR);
IPAddress dns(MYDNS);
IPAddress gw(MYGW);
IPAddress sn(MYIPMASK);
Ethernet.begin(macaddress, ip, dns, gw, sn);
Serial.println("STATIC OK!");
}
delay(5000);
Serial.print("Local IP : ");
Serial.println(Ethernet.localIP());
Serial.print("Subnet Mask : ");
Serial.println(Ethernet.subnetMask());
Serial.print("Gateway IP : ");
Serial.println(Ethernet.gatewayIP());
Serial.print("DNS Server : ");
Serial.println(Ethernet.dnsServerIP());
Serial.println("Ethernet Successfully Initialized");
EMailSender::EMailMessage message;
message.subject = "Soggetto";
message.message = "Ciao come stai<br>io bene.<br>www.mischianti.org";
// Send to 3 different email
const char* arrayOfEmail[] = {"destination1@gmail.com", "destination2@yahoo.com", "destination3@other.com"};
Serial.println("All structure created!");
EMailSender::Response resp = emailSend.send(arrayOfEmail, 3, message);
Serial.println("Sending status: ");
Serial.println(resp.status);
Serial.println(resp.code);
Serial.println(resp.desc);
}
void loop() {
// put your main code here, to run repeatedly:
}
Come puoi vedere, l’unica configurazione che devi applicare è la configurazione dell’account.
EMailSender emailSend("account@gmail.com", "<PASSWORD DELL'APPLICAZIONE>");
E naturalmente le destinazioni, in questo caso, c’è una lista di distribuzione
// Invia a 3 email diverse
const char* arrayOfEmail[] = {"destination1@gmail.com", "destination2@yahoo.com", "destination3@other.com"};
Serial.println("Tutta la struttura creata!");
EMailSender::Response resp = emailSend.send(arrayOfEmail, 3, message);
ma puoi inviare solo a una destinazione in questo modo.
EMailSender::Response resp = emailSend.send("account_to_send@gmail.com", message);
Invia Email con Gmail, STM32F4, w5500 e allegati da SD card
Per comprendere meglio questo esempio leggi questi articoli: “STM32: ethernet w5500 con plain (HTTP) e SSL (HTTPS)” e “Come usare la SD card con stm32 e la libreria SdFat”.
Configurazione della libreria
In questo esempio, attiviamo l’integrazione di SdFat con questa configurazione su EMailSenderKey.h
.
#ifndef DEFAULT_EMAIL_NETWORK_TYPE_STM32
#define DEFAULT_EMAIL_NETWORK_TYPE_STM32 NETWORK_W5100
#define DEFAULT_INTERNAL_STM32_STORAGE STORAGE_NONE
#define DEFAULT_EXTERNAL_STM32_STORAGE STORAGE_SDFAT2
#endif
E ricorda di decommentare l’attivazione di SSLClient.
#define SSLCLIENT_WRAPPER
Collegamento w5500 e adattatore SD card
Uso l’interfaccia SPI secondaria per usare la SD card.
Adattatori SD card AliExpress
STM32 | w5500 SPI1 |
---|---|
PA4 | CS |
PA5 | SCK |
PA6 | MISO |
PA7 | MOSI |
3.3v | 3.3v |
GND | GND |
STM32 | SD card SPI2 |
---|---|
PA12 | CS |
PA13 | SCK |
PA14 | MISO |
PA15 | MOSI |
3.3v | VCC |
GND | GND |
Sketch di esempio
Ecco il codice completo dello sketch, presta attenzione alla dichiarazione di Ethernet e SD.
/**
* EMailSender ^3.0.0 on STM32
*
* Send Email with GMail (application password see the article on how to configure)
* and attach a file from SD
*
* Need FLASH > 64K if you want use GMail with SSL
* alternatively you can use SendGrid without activation
* of SSLClient
*
* and ethernet w5500 SS
* SD on secondary SPI
*
To activate SSL you must uncomment
#define SSLCLIENT_WRAPPER
#ifndef DEFAULT_EMAIL_NETWORK_TYPE_STM32
#define DEFAULT_EMAIL_NETWORK_TYPE_STM32 NETWORK_W5100
#define DEFAULT_INTERNAL_STM32_STORAGE STORAGE_NONE
#define DEFAULT_EXTERNAL_STM32_STORAGE STORAGE_SDFAT2
#endif
*
* @author Renzo Mischianti <www.mischianti.org>
* @details www.mischianti.org
* @version 0.1
* @date 2022-03-22
*
* @copyright Copyright (c) 2022
*
*/
#include <Arduino.h>
#include <SPI.h>
#include <EthernetLarge.h>
#include <SdFat.h>
#include <sdios.h>
#include "EMailSender.h"
#define MACADDRESS 0x00,0x01,0x02,0x03,0x04,0x05
#define MYIPADDR 192,168,1,28
#define MYIPMASK 255,255,255,0
#define MYDNS 192,168,1,1
#define MYGW 192,168,1,1
// To use SD with secondary SPI
#define SD_CS_PIN PB12
static SPIClass mySPI2(PB15, PB14, PB13, SD_CS_PIN);
#define SD2_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)
SdFat sd;
uint8_t macaddress[6] = {MACADDRESS};
EMailSender emailSend("account@gmail.com", "<APPLICATION PASSWORD>");
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(100);
}
Serial.println("Begin SD");
// Secondary SPI for SD
if (!sd.begin(SD2_CONFIG)) {
// Primary SPI for SD
// if (!SD.begin(SD_CS_PIN)) {
Serial.println(F("initialization failed. Things to check:"));
Serial.println(F("* is a card inserted?"));
Serial.println(F("* is your wiring correct?"));
Serial.println(F("* did you change the chipSelect pin to match your shield or module?"));
while (1);
} else {
Serial.println(F("Wiring is correct and a card is present."));
}
// Show capacity and free space of SD card
Serial.print(F("Capacity of card: ")); Serial.print(long( sd.card()->sectorCount() >> 1 )); Serial.println(F(" kBytes"));
Serial.println("Begin Ethernet");
Ethernet.init(PA4);
if (Ethernet.begin(macaddress)) { // Dynamic IP setup
Serial.println("DHCP OK!");
}else{
Serial.println("Failed to configure Ethernet using DHCP");
// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
}
IPAddress ip(MYIPADDR);
IPAddress dns(MYDNS);
IPAddress gw(MYGW);
IPAddress sn(MYIPMASK);
Ethernet.begin(macaddress, ip, dns, gw, sn);
Serial.println("STATIC OK!");
}
Serial.println();
// To remove previous test
// SD.remove(F("/testCreate.txt"));
FsFile testFile = sd.open("/test.txt", O_WRITE);
if (testFile){
Serial.println("Write file content!");
testFile.print("Here the test text!! From SdFat2");
testFile.close();
}else{
Serial.println("Problem on create file!");
}
testFile.close();
delay(5000);
Serial.print("Local IP : ");
Serial.println(Ethernet.localIP());
Serial.print("Subnet Mask : ");
Serial.println(Ethernet.subnetMask());
Serial.print("Gateway IP : ");
Serial.println(Ethernet.gatewayIP());
Serial.print("DNS Server : ");
Serial.println(Ethernet.dnsServerIP());
Serial.println("Ethernet Successfully Initialized");
byte attachsNumber = 1;
EMailSender::FileDescriptior fileDescriptor[attachsNumber];
fileDescriptor[0].filename = F("test.txt");
fileDescriptor[0].url = F("/test.txt");
fileDescriptor[0].mime = MIME_TEXT_HTML;
fileDescriptor[0].storageType = EMailSender::EMAIL_STORAGE_TYPE_SD;
// fileDescriptor[1].filename = F("logo.jpg");
// fileDescriptor[1].url = F("/logo.jpg");
// fileDescriptor[1].mime = "image/jpg";
// fileDescriptor[1].encode64 = true;
// fileDescriptor[1].storageType = EMailSender::EMAIL_STORAGE_TYPE_LITTLE_FS;
EMailSender::Attachments attachs = {attachsNumber, fileDescriptor};
EMailSender::EMailMessage message;
message.subject = "Soggetto";
message.message = "Ciao come stai<br>io bene.<br>www.mischianti.org";
// Send to 3 different email
const char* arrayOfEmail[] = {"destination1@gmail.com", "destination2@yahoo.com", "destination3@other.com"};
Serial.println("All structure created!");
EMailSender::Response resp = emailSend.send(arrayOfEmail, 3, message, attachs);
// // Send to 3 different email, 2 in C and 1 in CC
// const char* arrayOfEmail[] = {"mischianti@gmail.com", "smtp.mischianti@gmail.com", "renzo.mischianti@gmail.com"};
// EMailSender::Response resp = emailSend.send(arrayOfEmail, 2, 1, message);
//
// // Send to 3 different email first to C second to CC and third to CCn
// const char* arrayOfEmail[] = {"mischianti@gmail.com", "smtp.mischianti@gmail.com", "renzo.mischianti@gmail.com"};
// EMailSender::Response resp = emailSend.send(arrayOfEmail, 3, message);
Serial.println("Sending status: ");
Serial.println(resp.status);
Serial.println(resp.code);
Serial.println(resp.desc);
}
void loop() {
// put your main code here, to run repeatedly:
}
Nello sketch, faccio la dichiarazione di SdFat all’interfaccia SPI secondaria
// Per usare SD con SPI secondario
#define SD_CS_PIN PB12
static SPIClass mySPI2(PB15, PB14, PB13, SD_CS_PIN);
#define SD2_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)
SdFat sd;
[...]
// SPI secondario per SD
if (!sd.begin(SD2_CONFIG)) {
// SPI primario per SD
// if (!SD.begin(SD_CS_PIN)) {
Serial.println(F("inizializzazione fallita. Cose da controllare:"));
Serial.println(F("* è inserita una carta?"));
Serial.println(F("* il tuo cablaggio è corretto?"));
Serial.println(F("* hai cambiato il pin chipSelect per adattarti al tuo shield o modulo?"));
while (1);
} else {
Serial.println(F("Il cablaggio è corretto e una carta è presente."));
}
E creo un file di testo
FsFile testFile = sd.open("/test.txt", O_WRITE);
if (testFile){
Serial.println("Scrivi contenuto file!");
testFile.print("Ecco il testo di prova!! www.mischianti.org Da SdFat2");
testFile.close();
}else{
Serial.println("Problema nella creazione del file!");
}
testFile.close();
poi genero un descrittore del file di allegato
EMailSender::FileDescriptior fileDescriptor[attachsNumber];
fileDescriptor[0].filename = F("test.txt");
fileDescriptor[0].url = F("/test.txt");
fileDescriptor[0].mime = MIME_TEXT_HTML;
fileDescriptor[0].storageType = EMailSender::EMAIL_STORAGE_TYPE_SD;
Invia Email con Gmail, STM32F4, enc28j60 e allegati da SPI Flash
Ora useremo un altro dispositivo standard per la connessione ethernet, l’enc28j60, e un set di IC di memoria come SPI Flash. Penso che tu debba leggere prima questi articoli: “STM32: ethernet enc28j60 con plain (HTTP) e SSL (HTTPS)” e “STM32: memoria SPI flash FAT FS”
Configurazione della libreria
In questo esempio, attiviamo l’integrazione di SdFat con questa configurazione su EMailSenderKey.h
.
#ifndef DEFAULT_EMAIL_NETWORK_TYPE_STM32
#define DEFAULT_EMAIL_NETWORK_TYPE_STM32 NETWORK_ETHERNET_ENC
#define DEFAULT_INTERNAL_STM32_STORAGE STORAGE_SPIFM
#define DEFAULT_EXTERNAL_STM32_STORAGE STORAGE_NONE
#endif
E ricorda di decommentare l’attivazione di SSLClient.
#define SSLCLIENT_WRAPPER
Collegamento enc28j60 e SPI Flash
Stiamo per usare STM32F4 Black-pill con il footprint integrato (se vuoi usare SPI Flash esterno leggi l’articolo relativo).
Qui i miei dispositivi ethernet testati w5500 lite - w5500 - enc26j60 mini - enc26j60 - lan8720
Attenzione devi usare PA3, non PA4.
ESP32 | w5500 |
---|---|
PA3 | CS |
PA5 | SCK |
PA6 | MISO |
PA7 | MOSI |
3.3v (meglio con 200mha esterni) | VCC |
GND | GND |
Aggiungo anche il cablaggio “manuale” se non usi questo dispositivo.
Ho testato questi SPI Flash per il nostro scopo w25q16 SMD 2Mb - w25q16 Discrete 2Mb - w25q32 SMD 4Mb - w25q32 Discrete 4Mb - w25q64 SMD 8Mb - w25q64 Discrete 8Mb - w25q128 SMD 16Mb - w25q128 Discrete 16Mb W25Q32 W25Q64 w25q128 module 4Mb 8Mb 16Mb
STM32F4 | SPI Flash |
---|---|
PA4 | CS |
PA6 | DI (IO1) |
PA7 | DI (IO0) |
PA5 | CLK |
3.3v | /WP |
3.3v | /Hold |
GND | GND |
3.3v | VCC |
Sketch di esempio
E infine il codice completo.
/**
* EMailSender ^3.0.0 on STM32
*
* Send Email with GMail (application password see the article on how to configure)
* and attach a file from SPI Flash
*
* Need FLASH > 64K if you want use GMail with SSL
* alternatively you can use SendGrid without activation
* of SSLClient
*
* and ethernet enc28j60 SS PA3
* SPI Flash on standard SPI with SS
*
To activate SSL you must uncomment
#define SSLCLIENT_WRAPPER
#ifndef DEFAULT_EMAIL_NETWORK_TYPE_STM32
#define DEFAULT_EMAIL_NETWORK_TYPE_STM32 NETWORK_ETHERNET_ENC
#define DEFAULT_INTERNAL_STM32_STORAGE STORAGE_SPIFM
#define DEFAULT_EXTERNAL_STM32_STORAGE STORAGE_NONE
#endif
*
* @author Renzo Mischianti <www.mischianti.org>
* @details www.mischianti.org
* @version 0.1
* @date 2022-03-22
*
* @copyright Copyright (c) 2022
*
*/
#include <Arduino.h>
#include <EthernetEnc.h>
#include "SdFat.h"
#include "Adafruit_SPIFlash.h"
#include <EMailSender.h>
Adafruit_FlashTransport_SPI flashTransport(SS, SPI); // Set CS and SPI interface
Adafruit_SPIFlash flash(&flashTransport);
// file system object from SdFat
FatFileSystem fatfs;
#define ETHERNET_CS_PIN PA3
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// Set the static IP address to use if the DHCP fails to assign
#define MYIPADDR 192,168,1,28
#define MYIPMASK 255,255,255,0
#define MYDNS 192,168,1,1
#define MYGW 192,168,1,1
EMailSender emailSend("account@gmail.com", "<APPLICATION PASSWORD>");
void setup() {
// Initialize serial port and wait for it to open before continuing.
Serial.begin(115200);
while (!Serial) {
delay(100);
}
Serial.println("Adafruit SPI Flash FatFs Full Usage Example");
// Initialize flash library and check its chip ID.
if (!flash.begin()) {
Serial.println("Error, failed to initialize flash chip!");
while(1) yield();
}
Serial.print("JEDEC ID: "); Serial.println(flash.getJEDECID(), HEX);
Serial.print("Flash size: "); Serial.println(flash.size());
Serial.flush();
// First call begin to mount the filesystem. Check that it returns true
// to make sure the filesystem was mounted.
if (!fatfs.begin(&flash)) {
Serial.println("Error, failed to mount newly formatted filesystem!");
Serial.println("Was the flash chip formatted with the SdFat_format example?");
while(1) yield();
}
Serial.println("Mounted filesystem!");
Serial.println();
// You can use Ethernet.init(pin) to configure the CS pin
Ethernet.init(ETHERNET_CS_PIN);
if (Ethernet.begin(mac)) { // Dynamic IP setup
Serial.println(F("DHCP OK!"));
}else{
Serial.println(F("Failed to configure Ethernet using DHCP"));
// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println(F("Ethernet shield was not found. Sorry, can't run without hardware. :("));
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println(F("Ethernet cable is not connected."));
}
IPAddress ip(MYIPADDR);
IPAddress dns(MYDNS);
IPAddress gw(MYGW);
IPAddress sn(MYIPMASK);
Ethernet.begin(mac, ip, dns, gw, sn);
Serial.println("STATIC OK!");
}
delay(5000);
Serial.print("Local IP : ");
Serial.println(Ethernet.localIP());
Serial.print("Subnet Mask : ");
Serial.println(Ethernet.subnetMask());
Serial.print("Gateway IP : ");
Serial.println(Ethernet.gatewayIP());
Serial.print("DNS Server : ");
Serial.println(Ethernet.dnsServerIP());
Serial.println("Ethernet Successfully Initialized");
Serial.println();
// To remove previous test
// SD.remove(F("/testCreate.txt"));
File testFile = fatfs.open("/test.txt", FILE_WRITE);
if (testFile){
Serial.println("Write file content!");
testFile.print("Here the test text!! From SPI Flash");
testFile.close();
}else{
Serial.println("Problem on create file!");
}
testFile.close();
delay(5000);
byte attachsNumber = 1;
EMailSender::FileDescriptior fileDescriptor[attachsNumber];
fileDescriptor[0].filename = F("test.txt");
fileDescriptor[0].url = F("/test.txt");
fileDescriptor[0].mime = MIME_TEXT_HTML;
fileDescriptor[0].storageType = EMailSender::EMAIL_STORAGE_TYPE_SPIFM;
// fileDescriptor[1].filename = F("logo.jpg");
// fileDescriptor[1].url = F("/logo.jpg");
// fileDescriptor[1].mime = "image/jpg";
// fileDescriptor[1].encode64 = true;
// fileDescriptor[1].storageType = EMailSender::EMAIL_STORAGE_TYPE_LITTLE_FS;
EMailSender::Attachments attachs = {attachsNumber, fileDescriptor};
EMailSender::EMailMessage message;
message.subject = "Soggetto";
message.message = "Ciao come stai<br>io bene.<br>www.mischianti.org";
// Send to 3 different email
const char* arrayOfEmail[] = {"destination1@gmail.com", "destination2@yahoo.com", "destination3@other.com"};
Serial.println("All structure created!");
EMailSender::Response resp = emailSend.send(arrayOfEmail, 3, message, attachs);
// // Send to 3 different email, 2 in C and 1 in CC
// const char* arrayOfEmail[] = {"mischianti@gmail.com", "smtp.mischianti@gmail.com", "renzo.mischianti@gmail.com"};
// EMailSender::Response resp = emailSend.send(arrayOfEmail, 2, 1, message);
//
// // Send to 3 different email first to C second to CC and third to CCn
// const char* arrayOfEmail[] = {"mischianti@gmail.com", "smtp.mischianti@gmail.com", "renzo.mischianti@gmail.com"};
// EMailSender::Response resp = emailSend.send(arrayOfEmail, 3, message);
Serial.println("Sending status: ");
Serial.println(resp.status);
Serial.println(resp.code);
Serial.println(resp.desc);
}
void loop() {
// put your main code here, to run repeatedly:
}
Il processo è lo stesso dell’esempio della SD card, iniziamo con la dichiarazione/configurazione.
Adafruit_FlashTransport_SPI flashTransport(SS, SPI); // Imposta CS e interfaccia SPI
Adafruit_SPIFlash flash(&flashTransport);
// oggetto file system da SdFat
FatFileSystem fatfs;
#define ETHERNET_CS_PIN PA3
poi l’inizializzazione
// Inizializza la libreria flash e controlla il suo chip ID.
if (!flash.begin()) {
Serial.println("Errore, non è stato possibile inizializzare il chip flash!");
while(1) yield();
}
Serial.print("ID JEDEC: "); Serial.println(flash.getJEDECID(), HEX);
Serial.print("Dimensione flash: "); Serial.println(flash.size());
Serial.flush();
// Prima chiama begin per montare il filesystem. Controlla che ritorni true
// per assicurarti che il filesystem sia stato montato.
if (!fatfs.begin(&flash)) {
Serial.println("Errore, non è stato possibile montare il filesystem appena formattato!");
Serial.println("Il chip flash è stato formattato con l'esempio SdFat_format?");
while(1) yield();
}
Serial.println("Filesystem montato!");
Serial.println();
Creo un file
File testFile = fatfs.open("/test.txt", FILE_WRITE);
if (testFile){
Serial.println("Scrivi contenuto file!");
testFile.print("Ecco il testo di prova!! Da SPI Flash");
testFile.close();
}else{
Serial.println("Problema nella creazione del file!");
}
testFile close();
E la dichiarazione dell’allegato
byte numeroAllegati = 1;
EMailSender::FileDescriptior fileDescriptor[numeroAllegati];
fileDescriptor[0].filename = F("test.txt");
fileDescriptor[0].url = F("/test.txt");
fileDescriptor[0].mime = MIME_TEXT_HTML;
fileDescriptor[0].storageType = EMailSender::EMAIL_STORAGE_TYPE_SPIFM;
// fileDescriptor[1].filename = F("logo.jpg");
// fileDescriptor[1].url = F("/logo.jpg");
// fileDescriptor[1].mime = "image/jpg";
// fileDescriptor[1].encode64 = true;
// fileDescriptor[1].storageType = EMailSender::EMAIL_STORAGE_TYPE_LITTLE_FS;
EMailSender::Allegati allegati = {numeroAllegati, fileDescriptor};
Abilita il provider SMTP di Gmail da usare con SSL
Per usare Gmail, devi creare una password per l’applicazione
- Vai alla pagina delle impostazioni di sicurezza di Gmail: https://myaccount.google.com/security
- Prima devi abilitare l’identificazione a 2 fattori.
- Poi devi creare una password per questa applicazione. La password è una stringa di 16 caratteri, copiala e usala come la tua password standard dell’account SMTP di Gmail!
Grazie
- STM32F1 Blue Pill: piedinatura, specifiche e configurazione IDE Arduino (STM32duino e STMicroelectronics)
- STM32: programmazione (STM32F1) via USB con bootloader STM32duino
- STM32: programmazione (STM32F1 STM32F4) tramite USB con bootloader HID
- STM32F4 Black Pill: pinout, specifiche e configurazione IDE Arduino
- STM32: ethernet w5500 standard (HTTP) e SSL (HTTPS)
- STM32: ethernet enc28j60 standard (HTTP) e SSL (HTTPS)
- STM32: WiFiNINA con un ESP32 come WiFi Co-Processor
- Come utilizzare la scheda SD con l’stm32 e la libreria SdFat
- STM32: memoria flash SPI FAT FS
- STM32: RTC interno, sistema orario e backup batteria (VBAT)
- STM32 LoRa
- STM32 Risparmio energetico
- STM32F1 Blue-Pill gestione clock e frequenza
- STM32F4 Black-Pill gestione clock e frequenza
- Introduzione e framework Arduino vs STM
- Libreria LowPower, cablaggio e Idle (STM Sleep).
- Sleep, deep sleep, shutdown e consumo energetico
- Sveglia da allarme RTC e Seriale
- Sveglia da sorgente esterna
- Introduzione al dominio di backup e conservazione delle variabili durante il RESET
- Registro di backup RTC e conservazione della SRAM
- STM32 invia email con allegati e SSL (come Gmail): w5500, enc28j60, SD e SPI Flash
- Server FTP su STM32 con W5500, ENC28J60, scheda SD e memoria flash SPI
- Collegamento dell’EByte E70 ai dispositivi STM32 (black/blue pill) e un semplice sketch di esempio