Thursday, 12 April 2012

Arduino Major / Minor Projects:

Arduino Major / Minor Project: 

As my major Arduino project I intend to build some type of data logger that will give the ability to log some type of data (Temperature, Humidity, Barometric pressure, Light etc) vs time To an SD card. This data will ideally be CSV formatted to make analysis easier. In order for this project to be a success I have determined that the following will need to be researched and then combined to form my project:
  • Reading External sensors with Arduino
  • Writing sensor values to SD card 
  • Writing a time stamp along with each sensor value
  • Keeping track of current time, Even during power loss
  • Storing persistent information on Arduino
  • Setting current date / time without using buttons or switches ( This is to ensure that the logger is simple and robust, no chance of mechanical failures.)
  • Setting a time period for sensor reading. 
  • PC software to facilitate setting time / date and logging periods without use of buttons.
  • Use JAVA or C# to send serial commands to Arduino.
The above tasks will be divided up to form both a minor and major project. The major project will build on the results of the minor project.
 
Part 1 Minor Project: 
  • Develop an intuitive GUI application to communicate with Arduino.
  • Set and persistently store time and date on Arduino.
  • Set logging time period and persistently store on Arduino.
Part 2 Major Project: 
  • Use Arduino to write to an SD card 
  • Create CSV file on SD card
  • Read Enviromental data using arduino
  • Write Environmental data to CSV file
  • Confirm that time and date is retained after power loss
  • Read logged data and confirm that it is not corrupted even after multiple power cycles
  • Interrogate the Arduino using the gui application to retrieve the current logging period / time. 
  • Provide user feedback
Developing a GUI Application to interface with Arduino:

The first step for my minor project is developing a GUI application that will allow an end user to communicate with the Arduino without needing any specialist knowledge, they should be able to attach the Arduino and quickly and easily carry out the following tasks:
  • Confirm Arduino is connected to PC
  • Set current time / date
  • Check current data logging period
  • Set / alter data logging period 
Initial application mock-up sketch:
 Application Interface Design:







 The application shown above has been developed in c# and functions as intended. It allows the Arduinos RTC to be synchronized with the current computer time and allows a data logging period to be set and retained even after multiple power cycles. Currently the RTC value is lost after each power cycle but this will be addressed at a later stage by using a battery to provide backup power to the RTC.


As a proof of concept a simple LED flasher was constructed, using the above application the led flashing period was able to be set and then saved onto the Arduinos EEPROM. When power is removed and reapplied the LED resumes flashing at the period specified previously.


Interface application source code:
The below source code is from the Arduino interface application, It is written in c# and operates as outlined above. 
/*
 * Created by SharpDevelop.
 * User: Tom Waymouth
 * Date: 29/03/2012
 * Time: 10:00 p.m.
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.IO.Ports;
using System.Windows.Forms;


namespace test2
{
    /// <summary>
    /// Description of MainForm.
    /// </summary>
    public partial class MainForm : Form
    {
        char[] buff = new char[1];
        string RxString;
        string currTime;
        Form help_frm = new help_frm();
        public MainForm()
        {
            //
            // The InitializeComponent() call is required for Windows Forms designer support.
            //
            InitializeComponent();




        //    textBox1.AppendText(currTime);


            string[] ports = SerialPort.GetPortNames();
            foreach(string port in ports)
            {
                comboBox2.Items.Add(port);
            }
            comboBox2.SelectedIndex = 0;
            Console.ReadLine();
        }



        void ButtonStartClick(object sender, EventArgs e)
        {
            serialPort1.PortName = comboBox2.SelectedItem.ToString();
              serialPort1.BaudRate = 9600;
 
              serialPort1.Open();
 
              if (serialPort1.IsOpen)
              {
                  setButtonState("enable");
                 probe();
    
     
              }
            

        }

        void ButtonStopClick(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)
              {
                    serialPort1.Close();
                  setButtonState("disable");
     

              }
        }

        void TextBox1KeyPress(object sender, KeyPressEventArgs e)
        {

              e.Handled = true;
        }

        void SerialPort1DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
             RxString = serialPort1.ReadExisting();
              this.Invoke(new EventHandler(DisplayText));
        }

        void MainFormFormClosing(object sender, FormClosingEventArgs e)
        {
             if (serialPort1.IsOpen) serialPort1.Close();
        }

        private void DisplayText(object sender, EventArgs e)
          {

            textBox1.AppendText(RxString);
          }

        void ComboBox1SelectionChangeCommitted(object sender, EventArgs e)
        {


        }
        void ComboBox2SelectionChangeCommitted(object sender, EventArgs e)
        {

        }

        public long DateTimeToUnixTimestamp(DateTime _DateTime)
        {
            TimeSpan _UnixTimeSpan = (_DateTime - new DateTime(197011000));
            return (long)_UnixTimeSpan.TotalSeconds;
        }
   

        void SyncTimeClick(object sender, EventArgs e)
        {
            currTime = "";
            long abc = DateTimeToUnixTimestamp(DateTime.Now);
            currTime = "T" + abc.ToString();
            buff[0] = 't';
                serialPort1.Write(buff, 01);
   
            for (int i = 0; i < currTime.Length; i++) {
   
                buff[0] = currTime[i];
                serialPort1.Write(buff, 01);
            }
        }
        void probe()
        {
            buff[0] = 'f';
            serialPort1.Write(buff, 01);
        }

        void Button1Click(object sender, EventArgs e)
        {

    }

        void ButtonStartMouseHover(object sender, EventArgs e)
        {
            toolTip1.Show("Connect to logger using specified COM port",this.buttonStart);
        }



        void TextBox2KeyPress(object sender, KeyPressEventArgs e)
        {
            if (char.IsLetter(e.KeyChar) ||
                char.IsSymbol(e.KeyChar) ||
                    char.IsWhiteSpace(e.KeyChar) ||
                    char.IsPunctuation(e.KeyChar))
        e.Handled = true;
        }

        void SyncTimeMouseHover(object sender, EventArgs e)
        {
            toolTip1.Show("Set Arduinos time to local computer time",this.syncTime);
        }

        void Button2Click(object sender, EventArgs e)
        {
            if (logging_period.Text != ""){
            if (Convert.ToInt16(logging_period.Text) <= 60 ){
            buff[0] = 'g';
                serialPort1.Write(buff, 01);
            for (int i = 0; i < logging_period.Text.Length; i++) {
                    //textBox1.AppendText(logging_period.Text[i].ToString());
                buff[0] = logging_period.Text[i];
                serialPort1.Write(buff, 01);

                }
                buff[0] = 'x';
                serialPort1.Write(buff, 01);
            }else{
                textBox1.AppendText("Error With Logging Period, please check your values");
            }
            }
        }
        void setButtonState(string state){
            if (state == "enable"){
                  buttonStart.Enabled = false;
                  buttonStop.Enabled = true;
                  textBox1.ReadOnly = false;
                  syncTime.Enabled = true;
                  button2.Enabled = true;
            }else if (state == "disable"){
                  buttonStart.Enabled = true;
                  buttonStop.Enabled = false;
                  textBox1.ReadOnly = true;
                  syncTime.Enabled = false;
                  button2.Enabled = false;
                  textBox1.Clear();
            }
        }

        void Help_btnClick(object sender, EventArgs e)
        {
            help_frm.Show();

        }



        void MainFormLoad(object sender, EventArgs e)
        {

        }
}

} 


SD Card interface:
One of the main goals of the next portion of my project revolves around using the Arduino to write data to an SD card. This stipulates building some come kind of interface that will allow me to interface the Arduino with an SD card. 


Whilst I could build an SD card interface circuit from scratch I have instead chosen the ada fruit "Logger shield" kit set which provides an SD card interface and a RTC battery backup provision. Whilst this kit has a number of short comings I feel I can modify it to work with my project.

Basic kit before assembly:
 
Kit after assembly:

 
Traditionally this kit would use wire jumpers to permanently connect the red and green leds to digital i/o pins 2 and 3. As  I would like the option to use these pins for other tasks if necessary I have instead added headers and plug wires to the kit to enhance its versatility.

Testing SD card kit:

 Once complete the sd card shield was tested using the sketch on this page: 
 http://arduino.cc/en/Tutorial/CardInfo

Once it was confirmed that the kit had been constructed correctly and that information about an SD card could be gathered using the above sketch  it was time to begin writing the main portion of the firmware that would allow the Arduino to interface with the application described above and record data onto an SD card at a preset interval. 

Main Arduino firmware:

The next step in my project involves writing a piece of firmware for the Arduino that will allow it to interface with the application described above, read environmental data and and log this data into a CSV file for further analysis. It will need to provide feedback to the end user in order to alert of any problems and also confirm that operations have been successfully completed. 


On completion of this firmware the Arduino should be able to log data to a SD card, Accept user input via the interface application and alert the user of any errors. 


If I get time I would also like to integrate an additional feature into the desktop interface application that would allow an end user to select what sensors to monitor via the software interface. 

Firmware Components:

1.Communication - Communicates with desktop application.
2.Timing - keep track of time and trigger events at defined times.
3.Storage - Writes data to SD card.
4. Sensor Interface - Interface with environmental sensors 

The firmware components will operate together in order to accomplish the goals set out in the project outline. Whilst performing separate functions the sections of firmware must not interface with the operation of any other component of the project.


Communication:


Allow the end user to connect the Arduino to the desktop application and carry out required operations.
Provide debug information if necessary via the serial interface (Debug will be enabled via a jumper, flashing red led means logger is in debug mode)


Timing:

Keep track of the date and time even if power is lost, provide timing to schedule events to ensure that no interference between sections of code occurs



Storage:


Store user preferences (logging intervals, Time / date) and also store any data received from external sensors


Sensor Interface:


Provide the ability to interface with environmental sensors and produce results that can be used by other sections of the firmware. 


Completed Firmware:  
 Below is the completed firmware that combines the various sections of the logger project and enables them to function as a cohesive unit:

Arduino Logger source code


Logger Test: 
The graphs below show a sample of data recovered using the completed logger project. The logger was set to record temperature and light at an interval of one sample every minute as outlined above the data was logged onto an SD card in CSV format. Once the SD card was removed from the logger the data was analyzed by 3rd party applications to produce the below graphs. 


 


Raw Data Sample:
Below is a sample of the raw data from the CSV file as created by the logger, this includes a time stamp, temperature and light:

stamp           datetime Temp Light Level
1335460310  "2012/4/26 17:11:50" 18.75 968
1335460370  "2012/4/26 17:12:50" 18.49 967
1335460430  "2012/4/26 17:13:50" 18.49 967
1335460490  "2012/4/26 17:14:50" 18.32 966
1335460551  "2012/4/26 17:15:51" 18.4 965
1335460611  "2012/4/26 17:16:51" 18.32 964
1335460671  "2012/4/26 17:17:51" 18.23 962
1335460731  "2012/4/26 17:18:51" 18.14 962
1335460791  "2012/4/26 17:19:51" 18.05 960
1335460851  "2012/4/26 17:20:51" 17.79 959
1335460911  "2012/4/26 17:21:51" 17.97 958
1335460971  "2012/4/26 17:22:51" 17.97 956
1335461031  "2012/4/26 17:23:51" 17.88 955
1335461091  "2012/4/26 17:24:51" 17.7 953
1335461152  "2012/4/26 17:25:52" 17.7 951
1335461212  "2012/4/26 17:26:52" 17.79 949
1335461272  "2012/4/26 17:27:52" 17.62 947
1335461332  "2012/4/26 17:28:52" 17.44 945
1335461392  "2012/4/26 17:29:52" 17.53 942
1335461452  "2012/4/26 17:30:52" 17.53 939
1335461512  "2012/4/26 17:31:52" 17.35 936




Potential Problems:
Whilst the completed logger functions an intended it initially had a couple of issues that I have made an effort to resolve or devise a potential solution that could be implemented given sufficient time.

 The first issue was that the logger would randomly loose the time and date and as a result the readings taken where corrupted. This problem was tracked down to a poor connection between the backup battery and its holder. The holder was physically larger than the battery and as a result the battery would come loose and the time would be lost. A small blob of solder placed under the battery resolved the issue and the logger has been keeping time since the fix was implemented.

The second issue faced was that the logger consumed a large amount of power (approximately 30mA) which whilst appearing tiny when compared to most electrical equipment is a large burden when it comes to battery powered equipment. Given a nominal battery voltage of 4.5V (3 x 1.5V) and a capacity of 2000 mAh the logger would last approximately 66 hours which would not be long enough for an extended deployment (weeks / months).


The first step in solving this problem was determining exactly what was using the bulk of the power. After reviewing the schematics for the Arduino UNO it was found that the Arduinos power supply was based on a linear regulator which regulates voltage by burning off any excess voltage as heat. This works fine for applications which run of mains power however it is a poor choice when it comes to battery powered applications.  


According to the datasheet for the ATmega328 (the heart of the Arduino UNO) it should only draw around 4mA of current when working and .03mA of current when sleeping. As the logger only needed to be "awake" when taking a reading it could theoretically spend a large portion of time sleeping and conserve a large amount of power. Ultimately this could give a battery life of many months.

To test this hypothesis a stand alone board was designed and built which would isolate the ATmega chip from the rest of the Arduino UNO circuitry.



With the chip running full time in the above board it drew 4mA of current which was a good improvement over the original 30mA and would give roughly 500 hours of battery life. This would however still not give months of life so some additional code was added to the chip which would put it to sleep between readings and save further power. 


With the sleep code in place the observed current reading was 0.03mA when sleeping and 4mA for a few Milli seconds when the chip woke and took readings. As the "wake" time is small it can be considered almost insignificant and should have very little impact on battery life. In fact the wake duration is so short that the increase of current cannot be picked up by the ammeter.

Under a best case scenario this would give a battery life of 66666 hours (2000mA / 0.03mA) or 2777 Days or 7.6 Years. I think that in this case the battery would self discharge long before the chip drained it. In any case even with an additional burden of external sensors and an SD card interface I think that months of battery life would be easily achievable.





Chip drawing 0.03mA during sleep




Project Reflection:


Overall  I felt that the major project went reasonably well bar a few minor software and hardware issues.

The software issues where initially quite challenging however once I added a debug routine that could be triggered via a jumper to my source code I found I could solve my issues relatively easily as the internal operation of the source code could be observed using the "Logger interface" application discussed above.

The hardware issues I came across are largely discussed above and were not issues as such but rather limitations of the Arduino Uno hardware platform. I researched these limitations and developed solutions that I feel could be fully integrated with my project given enough time. 

Was I to do something like this again I would spend more time looking into my chosen hardware platform before purchasing additional hardware to expand upon it. Given additional research time I think I could have got my project to a further, more refined state. In its current state it is fully functional however as discussed above it has a few minor issues which could be further refined given additional time.