Hi everyone,
I am a programming/Arduino novice, and am working on a project that would benefit from your expertise.
I am using An Arduino Uno, an Ethernet Shield 2, a magnetic window sensor, and an SR04 ultrasonic sensor. The premise is that the window opens and the SR04 sends out a ping to determine the distance. The distance will determine if the window was opened from inside the room or outside. If outside, an IP camera is accessed and a still image is saved to the SD card on the Ethernet shield. Immediately after saving the file, an email is sent out with a copy of the image attached.
Thanks to Koepel, I was able to solve some hefty memory issues and get this sketch to work fairly well. Now I want to add a second control item with a PIR sensor and a lamp attached to a PowerSwitch Tail, and this is where I am having some trouble. I have everything separated out into separate functions, but I am still being stymied by the fact that I cannot perform an action with one element and still check on the other. I have seen references to multitasking, and everything usually points to the same BlinkWithoutDelay example sketch. While this is helpful in terms of being able to see how to light up several LEDs in different sequences without using delay(), I am having trouble translating that into my own situation.
For example, even though the image file that I am saving is only 6K, it takes 12 seconds to save it to the SD card, and an additional 14 seconds to encode and attach to the email. The piece of code for saving the image looks like this:
// from the server, read them and print them: while (client.available()) { char c = client.read(); if (c == '\n' && currentLineIsBlank) { // end of HTTP header, now save requested file while (client.connected()) { // stay in this loop until the file has been received if (client.available()) { c = client.read(); // get file byte Serial.print(c); theFile.print(c); // save file byte } } } // detect the end of the incoming HTTP header if (c == '\n') { // starting a new line currentLineIsBlank = true; } else if (c != '\r') { // got a character on the current line currentLineIsBlank = false; } }
Note the "stay in this loop until the file has been received" comment. As long as this is taking place, I can wave my hand in front of the PIR sensor all I want and nothing happens. I understand that this is correct, but I want to know if there is any way of performing this image save, for example, and still react to the PIR sensor? I am suspecting that I may need to re-imagine how to use the PIR - perhaps have the opening of the window turn on the lamp via the PowerSwitch Tail, and then work with the PIR in cases where the window is not open.
Here's my entire code:
#include <SPI.h> #include <SD.h> #include <Ethernet2.h> #include <NewPing.h> // Arduino pin tied to trigger pin on the ultrasonic sensor. #define TRIGGER_PIN 8 // Arduino pin tied to echo pin on the ultrasonic sensor. #define ECHO_PIN 7 // Maximum distance we want to ping for (in centimeters). Maximum sensor //distance is rated at 400-500cm. #define MAX_DISTANCE 200 // window sensor pin #define WindowPin 6 //variable to hold the distance to the "window" int distance; // NewPing setup of pins and maximum distance. NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); byte mac[] = { 0x90, 0xA2, 0xDA, 0x10, 0x7A, 0x87 }; IPAddress server(192, 168, 1, 27); IPAddress ip(192, 168, 1, 100); char mailServer[] = "mail.smtp2go.com"; EthernetClient client; boolean currentLineIsBlank = true; boolean needPicture = true; File theFile; File sendFile; //variable to use for encoding process static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char buffer[80]; void setup() { Serial.begin(9600); Serial.println(F("Starting the serial monitor")); pinMode(WindowPin, INPUT_PULLUP); //**************************************** //new on 8/31 // disable Ethernet chip pinMode(10, OUTPUT); digitalWrite(10, HIGH); Serial.print(F("Initializing SD card...")); if (!SD.begin(4)) { Serial.println(F(" initialization failed!")); return; } Serial.println(F(" initialization done.")); //***************************************** // start the Ethernet connection: if (Ethernet.begin(mac) == 0) { Serial.println(F("Failed to configure Ethernet using DHCP")); // no point in carrying on, so do nothing forevermore: // try to congifure using IP address instead of DHCP: Ethernet.begin(mac, ip); } // give the Ethernet shield a second to initialize: delay(1000); // print your local IP address: Serial.println(Ethernet.localIP()); } void loop() { checkWindowSensor(); } void checkWindowSensor() { //take a read of the WindowPin. If it reads HIGH, the window is open, //and a message can be displayed as such if(digitalRead(WindowPin) == HIGH) { Serial.println(F("The window is open.")); measureDistance(); //delay for 2 seconds, to avoid incessant scrolling delay(2000); } //If the sensor reads LOW, the window is closed, and a message can be //displayed as such. No need to involve the ultrasonic sensor else { Serial.println(F("Window is closed.")); //this window is closed, so we will need a picture if it opens again needPicture = true; //delay for 2 seconds, to avoid incessant scrolling delay(2000); } } void measureDistance() { // Wait 50ms between pings (about 20 pings/sec). 29ms should be the //shortest delay between pings. delay(50); Serial.print(F("Ping: ")); // Send ping, get distance in cm and print result (0 = outside set distance range) distance = sonar.ping_cm(); Serial.print(distance); Serial.print(F("cm")); //if the distance to the "window" is less than 15cm, indicate that it was //opened from inside. Otherwise, insicate that it was opened from outside if(distance <15) { Serial.println(F(" --opened from inside")); } else { Serial.println(F(" --opened from outside")); //this window was previously closed, so we need a picture if (needPicture == true) takePicture(); } } void takePicture() { Serial.println(F("Taking picture")); //prevent repeat picture while window stays open needPicture = false; Serial.println(F("connecting to camera...")); // if you get a connection, report back via serial: if (client.connect(server, 80)) { Serial.println(F("connected")); // Make a HTTP request: strcpy_P(buffer, PSTR("GET /image.jpg\r\n")); client.write(buffer); strcpy_P(buffer, PSTR("Authorization: Basic YWRtaW46YXVndXN0MTA=\r\n")); client.write(buffer); strcpy_P(buffer, PSTR("Host: 192.168.1.27\r\n")); client.write(buffer); //client.println("Connection: close"); client.println(); delay(1000); } else { // if you didn't get a connection to the server: Serial.println(F("connection failed")); } // open the file for writing Serial.println(F("Creating file.")); theFile = SD.open("pic.jpg", FILE_WRITE); // change file name to write to here if (!theFile) { Serial.println(F("Could not create file")); while (1); } // from the server, read them and print them: while (client.available()) { char c = client.read(); if (c == '\n' && currentLineIsBlank) { // end of HTTP header, now save requested file while (client.connected()) { // stay in this loop until the file has been received if (client.available()) { c = client.read(); // get file byte Serial.print(c); theFile.print(c); // save file byte } } } // detect the end of the incoming HTTP header if (c == '\n') { // starting a new line currentLineIsBlank = true; } else if (c != '\r') { // got a character on the current line currentLineIsBlank = false; } } // if the server's disconnected, stop the client: if (!client.connected()) { Serial.println(); Serial.println(F("disconnecting.")); client.stop(); theFile.close(); Serial.println(F("Finished writing to file")); if(sendEmail()) Serial.println(F("Email Sent")); else Serial.println(F("Email Failed")); //while(true); // do nothing forevermore: //while (true); } } byte sendEmail(){ byte thisByte = 0; byte respCode; if(client.connect(mailServer, 80) == 1){ Serial.println(F("Connected")); } else { Serial.println(F("Connection Failed")); return 0; } if(!eRcv()) return 0; Serial.println(F("Sending Hello")); //replace 1.2.3.4 with your Arduino's ip************************** strcpy_P(buffer, PSTR("EHLO\r\n")); client.write(buffer); if(!eRcv()) return 0; Serial.println(F("Sending authorizes login")); strcpy_P(buffer, PSTR("auth login\r\n")); client.write(buffer); if(!eRcv()) return 0; Serial.println(F("Sending User")); //Change to your base64 encoded user************************** strcpy_P(buffer, PSTR("Y29tcDQ0NC5kb25mcmFua3JpY2U=\r\n")); client.write(buffer); if(!eRcv()) return 0; Serial.println(F("Sending Password")); //change to your base64 encoded password************************** strcpy_P(buffer, PSTR("YXJkdWlub1J1bGVzIQ==\r\n")); client.write(buffer); if(!eRcv()) return 0; //change to your email address (sender)************************** Serial.println(F("Sending From")); strcpy_P(buffer, PSTR("MAIL From: <donny.frankrice@gmail.com>\r\n")); client.write(buffer); if(!eRcv()) return 0; //change to recipient address************************** Serial.println(F("Sending To")); strcpy_P(buffer, PSTR("RCPT To: <comp444.donfrankrice@gmail.com>\r\n")); client.write(buffer); if(!eRcv()) return 0; Serial.println(F("Sending DATA")); strcpy_P(buffer, PSTR("DATA\r\n")); client.write(buffer); if(!eRcv()) return 0; Serial.println(F("Sending email")); //change to recipient address************************** strcpy_P(buffer, PSTR("To: Donny <comp444.donfrankrice@gmail.com>\r\n")); client.write(buffer); //change to your address************************** strcpy_P(buffer, PSTR("From: Donny's Arduino <donny.frankrice@gmail.com>\r\n")); client.write(buffer); strcpy_P(buffer, PSTR("Subject: Alert! Possible Intruder!\r\n")); client.write(buffer); //**************************************************** //Start of Attach File sendFile =SD.open("pic.jpg",FILE_READ); strcpy_P(buffer, PSTR("Content-Type: image/jpg; name=\"pic.jpg\"\r\n")); client.write(buffer); client.write("Content-Disposition: attachment; filename=pic.jpg\r\n"); strcpy_P(buffer, PSTR("Content-Transfer-Encoding: base64\r\n\r\n")); client.write(buffer); encode(); sendFile.close(); //End of Attach File //**************************************************** client.print("\r\n.\r\nQUIT\n"); if(!eRcv()) return 0; client.stop(); Serial.println(F("disconnected")); //remove picture file in preparation for any future image capture SD.remove("pic.jpg"); return 1; } byte eRcv(){ byte respCode; byte thisByte; int loopCount = 0; while(!client.available()){ delay(1); loopCount++; // if nothing received for 10 seconds, timeout if(loopCount > 10000) { client.stop(); Serial.println(F("\r\nTimeout")); return 0; } } respCode = client.peek(); while(client.available()){ thisByte = client.read(); Serial.write(thisByte); } if(respCode >= '4'){ efail(); return 0; } return 1; } void efail(){ byte thisByte = 0; int loopCount = 0; strcpy_P(buffer, PSTR("QUIT\r\n")); client.write(buffer); while(!client.available()) { delay(1); loopCount++; // if nothing received for 10 seconds, timeout if(loopCount > 10000) { client.stop(); Serial.println(F("\r\nTimeout")); return; } } while(client.available()){ thisByte = client.read(); Serial.write(thisByte); } client.stop(); Serial.println(F("isconnected")); } 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 (sendFile.available()!=0) { len=0; for (i=0;i<3;i++){ in[i]=(unsigned char) sendFile.read(); if (sendFile.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||sendFile.available()==0){ if (blocksout) client.print("\r\n"); blocksout=0; } } }
Thanks for your time! I welcome your advice!
Donny