Go Down

Topic: Email attachments from arduino as an SMTP client (Read 14912 times) previous topic - next topic

btsp

okay i figured that out too, here's a post Incase someone is wondering.
Sending Multiple Attachments

Code: [Select]

    Serial.println("connected");
 
                       client.println("HELO itismeletschat"); /* say hello (statement after helo is needed but irrelevant)*/
                         delay(wait); /* wait for a response */

                       client.println("MAIL From: blank@gmx.com"); /* identify sender, this should be the same as the smtp server you are using */
                         delay(wait); /* wait for a response */

                       client.println("RCPT To: blank@virelec.com"); /* identify recipient */
                         delay(wait); /* wait for a response */

                       client.println("DATA");
                         delay(wait); /* wait for a response */
                       
                       client.println("Subject: text file attached");
                       
                       [b]client.print("Content-type: multipart/mixed; boundary=\"$boundary\" \r\n");[/b]
                       
                       [b]client.print("--$boundary\r\n");[/b]
                       
                       datatxt =SD.open("HEAD2.csv",FILE_READ);                
                       client.print("Content-Type: csv; name=\"HEAD2.csv\"\r\nContent-Disposition: attachment; filename=\"HEAD2.csv\"\r\nContent-Transfer-Encoding: base64\r\n\r\n");
                       encode();
                       datatxt.close();
                       delay(wait);
                       
                       datatxt =SD.open("meow.txt",FILE_READ);
                       [b]client.print("--$boundary\r\n");[/b]
                       client.print("Content-Type: txt; name=\"meow.txt\"\r\nContent-Disposition: attachment; filename=\"meow.txt\"\r\nContent-Transfer-Encoding: base64\r\n\r\n");
                       encode();
                       datatxt.close();
                       
                       client.println("."); /* end email */
                       client.println("QUIT"); /* terminate connection */
                       delay(wait); /* wait for a response */
           
           

PaulS

Code: [Select]
delay(wait); /* wait for a response */
If you are waiting for a response, shouldn't you then read the response? Or else remove the incorrect comment. Even better, remove the useless delay() calls.

primate


Code: [Select]
delay(wait); /* wait for a response */
If you are waiting for a response, shouldn't you then read the response? Or else remove the incorrect comment. Even better, remove the useless delay() calls.


Some [older] SMTP servers don't support pipelining (i.e. receiving a new command before the current command is responded to), and even for servers that support pipelining, some commands are in herently not pipelineable, so sending commands in quick succession without a bit of delay might be problematic (especially if the sketch is not handling server errors. It might be safer to keep some sort of delay.

Not reading the server response should not be a big deal and the receive buffer should just fill up with unread responses. Downside is of course it can't process server errors. I'm not sure how the Serial stack on the Arduino handles full buffers but if it follows general parctice it will just silently drop the new bytes.

I think the safer way (if reliability is an issue), is to replace the constant wait with a function call that loops to wait for client.available() > 0, and just read and discard the bytes beefore sending the next command. This ensures the server had responded, and to account for transient network delays that causes replies to come back more than the constant wait period.

I realized that I might be getting off topic here ...

PaulS

Quote
I realized that I might be getting off topic here ...

No, I think you are properly discussing the actions that need to be performed, instead of just blindly waiting what might be just enough time, not near enough time, or way too long.

dazer

Hi,
So testing the above code, I still cannot get any "attachment" to my email.

Code: [Select]

  client.println("Subject: Arduino attachment email test\r\n");
  picture = SD.open("data.txt", FILE_READ);
  client.print("Content-Type: txt; name=\"data.txt\"\r\nContent-Disposition: attachment; filename=\"data.txt\"\r\nContent-Transfer-Encoding: base64\r\n\r\n");
  encode();
  client.println("."); /* end email */

  if(!eRcv()) return 0;

  Serial.println(F("Sending QUIT"));
  strcpy_P(tBuf,PSTR("QUIT\r\n"));  
  client.write(tBuf);
  if(!eRcv()) return 0;

  client.stop();

  Serial.println(F("disconnected"));

  return 1;
}


void encodeblock(unsigned char in[3], unsigned char out[4], int len) {
  out[0] = cb64[in[0] >> 2]; out[1] = cb64[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)];
  out[2] = (unsigned char) (len > 1 ? cb64[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6)] : '=');
  out[3] = (unsigned char) (len > 2 ? cb64[in[2] & 0x3F] : '=');
}
void encode() {
  unsigned char in[3], out[4]; int i, len, blocksout = 0;
  while (picture.available() != 0) {
    len = 0; for (i = 0; i < 3; i++) {
      in[i] = (unsigned char) picture.read();
      if (picture.available() != 0) len++;
      else in[i] = 0;
    }
    if (len) {
      encodeblock(in, out, len);
      for (i = 0; i < 4; i++) client.write(out[i]);
      blocksout++;
    }
    if (blocksout >= 19 || picture.available() == 0) {
      if (blocksout) client.print("\r\n");
      blocksout = 0;
    }
  }
}



This code just inserts some garbage characters on my email. It should ideally attach the file data.txt

Code: [Select]

.....
 client.print("Content-Type: txt; name=\"data.txt\"\r\nContent-Disposition: attachment; filename=\"data.txt\"\r\nContent-Transfer-Encoding: base64\r\n\r\n");
 encode();
 client.print("\r\n.\r\nQUIT\n");
 client.println("."); /* end email */
 client.stop();
 datatxt.close();
 while(1);
}



void encodeblock(unsigned char in[3],unsigned char out[4],int len) {
 out[0]=cb64[in[0]>>2]; out[1]=cb64[((in[0]&0x03)<<4)|((in[1]&0xF0)>>4)];
 out[2]=(unsigned char) (len>1 ? cb64[((in[1]&0x0F)<<2)|((in[2]&0xC0)>>6)] : '=');
 out[3]=(unsigned char) (len>2 ? cb64[in[2]&0x3F] : '=');
}

void encode() {
 unsigned char in[3],out[4];
 int i,len,blocksout=0;
 
 while (datatxt.available()!=0) {
   len=0;
     for (i=0;i<3;i++){
           in[i]=(unsigned char) datatxt.read();
               if (datatxt.available()!=0) len++;
                     else in[i]=0;
     }
     if (len){
         encodeblock(in,out,len);
          for(i=0;i<4;i++) client.write(out[i]);
               blocksout++; }
     if (blocksout>=19||datatxt.available()==0){
         if (blocksout) client.print("\r\n");  blocksout=0;
     }
  }
}



This code on the other hand does the 64 bit encoding but it does not appear as an attachment. It just prints the values in the body of my email.

Am I understanding this wrong an attachment would not be an "attachment" but would appear in the body of the email?

SurferTim

This is how I send a text file attachment from my SD card.
Code: [Select]
  client.println("Subject: Arduino email image");

// this is new
  client.write("MIME-Version: 1.0\r\n");
  client.write("Content-Type: Multipart/mixed; boundary=frontier\r\n\r\n");
 
  client.write("--frontier\r\n");
  client.write("Content-Type: text/plain\r\n\r\n");
 
  client.write("This is an inage from my Arduino!\r\n");

  client.write("--frontier\r\n");
  client.write("Content-Type: text/plain\r\n");
  client.write("Content-Disposition: attachment; filename=test.txt\r\n\r\n");

// this is where you would send your file
  int clientCount = 0;

  File myFile = SD.open("test.txt");
 
  if(myFile) {
    while(myFile.available()) {
      clientCount = myFile.read(tBuf,64);
      client.write((byte*)tBuf,clientCount);
    }
    myFile.close();                 
  }
  else {
    Serial.println(F("File open failed"));
  }

  client.write("--frontier--\r\n");

// end of new
// send a period on a line by itself
  client.println(".");

dazer

Hey Tim, that works a charm :)  on text messages. However when I tried to send an image the image is sent all smudged up:

The code is used is:
Code: [Select]

// this is new
  client.write("MIME-Version: 1.0\r\n");
  client.write("Content-Type: Multipart/mixed; boundary=frontier\r\n\r\n");
 
  client.write("--frontier\r\n");
  client.write("Content-Type: image/jpg\r\n\r\n");
 
  client.write("This is an image from my Arduino!\r\n");

  client.write("--frontier\r\n");
  client.write("Content-Type: image/jpg\r\n");
  client.write("Content-Disposition: attachment; filename=picture.jpg\r\n\r\n");

// this is where you would send your file
  int clientCount = 0;

//  File myFile = SD.open("test.txt");
  File myFile = SD.open("picture.jpg");
  if(myFile) {
    while(myFile.available()) {
      clientCount = myFile.read(tBuf,64);
      client.write((byte*)tBuf,clientCount);
    }
    myFile.close();                 
  }
  else {
    Serial.println(F("File open failed"));
  }

  client.write("--frontier--\r\n");

// end of new
// send a period on a line by itself
  client.println(".");

  if(!eRcv()) return 0;

  Serial.println(F("Sending QUIT"));
  strcpy_P(tBuf,PSTR("QUIT\r\n")); 
  client.write(tBuf);
  if(!eRcv()) return 0;

  client.stop();

  Serial.println(F("disconnected"));


Any pointers ? Thanks a ton.

dazer

Ok just wondering
Code: [Select]

  if(myFile) {
    while(myFile.available()) {
      clientCount = myFile.read(tBuf,64);
      client.write((byte*)tBuf,clientCount);
    }


If i play with the value 64 the email does not get sent even if I adjust char tBuf[64];

Can I do away with the buffer concept altogether or increase its size while sending the image?


SurferTim

I think you must base64 encode the image to send it as an email attachment. Don't have much time to test today, but I'll work on it when I can.

dazer

Thank you for your reply Tim.  I did try the base 64 encoding using the code posted by Razorblade but was not successful. However, i did notice that when I did send images using the code you posted .txt files, the resultant files were not the same.
I opened both the original file and the attachment of the same file that i received in my email in a hex editor.
0x0D in the original were replaced by 0x0A in the attachment at various locations.

I have attached both the files.

Quote
I think you must base64 encode the image to send it as an email attachment.
Why is this usually done?



Thanks in advance.


aarg

Why is this usually done?
Because many byte values are not printable characters.
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

dazer

Ah! now that makes sense. Thank you for your reply.

dazer

Tim, I kinda figured out a few things out.
Firstly if i remove the
Code: [Select]

    if (blocksout >= 19 || picture.available() == 0) {
      if (blocksout) client.print("\r\n");
      blocksout = 0;

the base 64 encoding gets properly fomatted.
2) In your code the last SMTP command
Code: [Select]

  client.write("--frontier--\r\n");

gets inserted into the attached jpeg file at the end and hence the received file become unreadable.

This is my code snippet:
Code: [Select]

client.println("Subject: Arduino email encoded image");

  // this is new
  client.write("MIME-Version: 1.0\r\n");
  client.write("Content-Type: Multipart/mixed; boundary=frontier\r\n\r\n");

  client.write("--frontier\r\n");
  client.write("Content-Type: text/plain\r\n\r\n");

  client.write("This is a Image encoded attachment from my Arduino!\r\n");

  client.write("--frontier\r\n");
  client.write("Content-Type: image/jpeg\r\n");
  client.write("Content-Disposition: attachment; filename=picture.jpg\r\n");
  client.write("Content-Transfer-Encoding: base64\r\n\r\n");

  // this is where you would send your file
  int clientCount = 0;

  myFile = SD.open("picture.jpg");

  if (myFile) {
    while (myFile.available()) {
//      clientCount = myFile.read(tBuf, 64);
//      client.write((byte*)tBuf, clientCount);
      encode();
    }
    myFile.close();
  }
  else {
    Serial.println(F("File open failed"));
    while(1);
  }
//  client.write("\n"); //added
  client.write("--frontier--\r\n");

  // end of new
  // send a period on a line by itself
  client.println(".");
  

  if (!eRcv()) return 0;

  Serial.println(F("Sending QUIT"));
  strcpy_P(tBuf, PSTR("QUIT\r\n"));
  client.write(tBuf);
  if (!eRcv()) return 0;

  client.stop();

  Serial.println(F("disconnected"));

  return 1;


void encodeblock(unsigned char in[3], unsigned char out[4], int len) {
  out[0] = cb64[in[0] >> 2]; out[1] = cb64[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)];
  out[2] = (unsigned char) (len > 1 ? cb64[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6)] : '=');
  out[3] = (unsigned char) (len > 2 ? cb64[in[2] & 0x3F] : '=');
}

void encode() {
  unsigned char in[3], out[4]; int i, len, blocksout = 0;
  while (myFile.available() != 0) {
    len = 0; for (i = 0; i < 3; i++) {
      in[i] = (unsigned char) myFile.read();
      if (myFile.available() != 0) len++;
      else in[i] = 0;
    }
    if (len) {
      encodeblock(in, out, len);
      for (i = 0; i < 4; i++) client.write(out[i]);
      blocksout++;
    }
//    if (blocksout >= 19 || myFile.available() == 0) {
//      if (blocksout) client.print("\r\n");
//      blocksout = 0;
//    }
  }
}
}


Any clues?

Zyphel

I'm using this encode routine from Razorblades post #18 in this thread (thanks...quoted below) and sending an email with attachment successfully, but I'm getting nowhere near the 50kb/30second speed indicated.
Running on a mega2560 with cc3000 wifi board sending a 5k text file is taking 3 minutes to encode.
Has anyone else seen this kind of time to encode?


(from Post #18)
This would be the whole program. Reads picture.jpg from microSD card, sends an e-mail with picture.jpg attachment:
Code: (example.ino) [Select]
#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>

byte MAC[]={0x,0x,0x,0x,0x,0x};  // Your MAC
File picture; Client client;

static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
void encodeblock(unsigned char in[3],unsigned char out[4],int len) {
  out[0]=cb64[in[0]>>2]; out[1]=cb64[((in[0]&0x03)<<4)|((in[1]&0xF0)>>4)];
  out[2]=(unsigned char) (len>1 ? cb64[((in[1]&0x0F)<<2)|((in[2]&0xC0)>>6)] : '=');
  out[3]=(unsigned char) (len>2 ? cb64[in[2]&0x3F] : '=');
}
void encode() {
  unsigned char in[3],out[4]; int i,len,blocksout=0;
  while (picture.available()!=0) {
    len=0; for (i=0;i<3;i++) { in[i]=(unsigned char) picture.read(); if (picture.available()!=0) len++; else in[i]=0; }
    if (len) { encodeblock(in,out,len); for(i=0;i<4;i++) client.write(out[i]); blocksout++; }
    if (blocksout>=19||picture.available()==0) { if (blocksout) client.print("\r\n");  blocksout=0; }
  }
}

void setup() {
  Ethernet.begin(MAC); delay(2000);
  pinMode(10,OUTPUT); SD.begin(4);
}

void loop() {
  picture=SD.open("picture.jpg",FILE_READ);
  client.connect("smtp.mail.yahoo.com",25);
  client.print("HELO\nAUTH PLAIN\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n");  // x: base64 encoded login: \0foo@yahoo.com\0password
  client.print("MAIL FROM:<foo@yahoo.com>\nRCPT TO:<bar@fakemail.com>\nDATA\nFrom: \"Foo\" <foo@yahoo.com>\r\nTo: bar@fakemail.com\r\nSubject: Picture attached\r\n");
  client.print("Content-Type: image/jpeg; name=\"picture.jpg\"\r\nContent-Disposition: attachment; filename=\"picture.jpg\"\r\nContent-Transfer-Encoding: base64\r\n\r\n");
  encode();
  client.print("\r\n.\r\nQUIT\n");
  client.stop();
  picture.close();
  while(1);
}


Takes less than 30 seconds to send a 50 KiB picture.
[/quote]

idp1

hey Tim, may i know what what does tbuf refer to ? it is on this line

Code: [Select]
while(myFile.available()) {
      clientCount = myFile.read(tBuf,64);


i have error when compiling code, stat:


send.ino: In function 'void setup()':
send:72: error: 'tBuf' was not declared in this scope
'tBuf' was not declared in this scope

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy