Syncing a DS1307 automatically using python

My real time clock (DS1307) arrived yesterday. Before I had this I was using the Time package to set and get time stamps for my data collection. There was one big problem I had with the hole syncing of the time. It had to be done manually. It seemed impossible for me to get the seconds from 1970, then paste them into the serial command and do all this within a few seconds. There had to be a better way. I found a post on the Arduino wiki, showing how to set the time using NTP. This was not an option as I don’t have an ethernet shield. I decided to have a look into using c++ to set the time, but then discovered platform dependancies which would complicate things. I decided to go with python. Disclaimer: I have extremely little experience with python, let alone Arduino (c code). I did a bit of research about communicating between Arduino and python. Thanks to this page it wasn’t hard to get started. With alot of trial and error (probably should have payed more attention to that link), I figured out that every Serial.print on the Arduino side must have new line at the end, by using either Serial.println() or the ‘\n’ symbol.

To sync the Arduino’s RTC you need two scripts, a python script and a script for the Arduino. From here on I assuming you read that pervious link and tried it out. I don’t remember seeing it mentioned on that page but you require the addition pyserial library for this to work.

On the Python side

#!/usr/bin/python
#The libraries required
import serial  
import time  
import datetime

#A list (not in my case) of possible arduino devices.
locations=['/dev/tty.usbserial-A80090pL']

###Unless it's needed, I think it's best to let the arduino handle the delays

for device in locations:  
	try:  
		print "Trying...",device  
		arduino = serial.Serial(device, 9600)
		#Print the response from the Arduino - to show the communiction has started.
		print arduino.readline()	
		break  
	except:  
		print "Failed to connect on",device     
  
try:  
	#Get the current time
	t = datetime.datetime.now();
	#Send the current time to the Arduino
	arduino.write(t.strftime("%Y-%m-%d %H:%M:%S"))
	#Receive the time string from above back from the Ardunio.
	print arduino.readline()
	#Receive what the minute is from the DS1307. 
	print arduino.readline()

except:
	print "Failed to send!"
 

The above code, creates a serial connection with the Arduino, based on the USB device specified in locations. It is possible to list several seperated with a comma. It then gets the system time, and then passes this to the Arduino.

On the Arduino side
The arduino requires a script that will receive the string of numbers from the serial connection and convert them to ints and pass them to the RTC. I used a function I found on a forum to convert the string to int values. These are then passed to the RTC syncing functions within the DS1307new library. In theory this script would also work without the use of the python script, by just using a serial monitor window from within the Arduino IDE.

/*
 Author: Jason Lessels
 Date created: 19/06/2011
 License: GPL (>=2)
 Description: A script to receive A time input from a serial communication to set the time. The time format
 must be in 'yyyy mm dd hh dd ss' format for this to work.
 */

#include <Wire.h> 
#include <DS1307new.h>


String dateIn;
int TimeSet = 0;
void setup(){
	Serial.begin(9600);
	delay(100);
	Serial.print("I hear you\n");
}



void loop(){
	if(Serial.available()==19&&TimeSet==0){
		for(int i=0;i<19;i++){
			dateIn += String(byte(Serial.read()));
			
		}
		RTC.stopClock();
		RTC.fillByYMD(getInt(dateIn.substring(0,4)),getInt(dateIn.substring(5,7)),getInt(dateIn.substring(8,10)));
		RTC.fillByHMS(getInt(dateIn.substring(11,13)),getInt(dateIn.substring(14,16)),getInt(dateIn.substring(17,19)));
		RTC.setTime();
		RTC.startClock();
		Serial.println(dateIn);
		RTC.getTime();
		Serial.println(RTC.second,DEC);
		TimeSet=1;
		
		
	}
}

/*
 -------Functions-------
 I obtained the following function from
//http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1285085509
 */
int getInt(String text)
{
	char temp[6];
	text.toCharArray(temp, 5);
	int x = atoi(temp);
	return x;
} 

Check to see if the time is synced
A short script to check display the time through a serial connection.

#include <Wire.h>                       
#include <DS1307new.h>

void setup(){
	Serial.begin(9600);
	delay(100);
	Serial.println("What time is it in Arduino world?");
}


void loop()
{
	RTC.getTime();
	if (RTC.hour < 10)                    // correct hour if necessary
	{
		Serial.print("0");
		Serial.print(RTC.hour, DEC);
	} 
	else
	{
		Serial.print(RTC.hour, DEC);
	}
	Serial.print(":");
	if (RTC.minute < 10)                  // correct minute if necessary
	{
		Serial.print("0");
		Serial.print(RTC.minute, DEC);
	}
	else
	{
		Serial.print(RTC.minute, DEC);
	}
	Serial.print(":");
	if (RTC.second < 10)                  // correct second if necessary
	{
		Serial.print("0");
		Serial.print(RTC.second, DEC);
	}
	else
	{
		Serial.print(RTC.second, DEC);
	}
	Serial.print(" ");
	if (RTC.day < 10)                    // correct date if necessary
	{
		Serial.print("0");
		Serial.print(RTC.day, DEC);
	}
	else
	{
		Serial.print(RTC.day, DEC);
	}
	Serial.print("-");
	if (RTC.month < 10)                   // correct month if necessary
	{
		Serial.print("0");
		Serial.print(RTC.month, DEC);
	}
	else
	{
		Serial.print(RTC.month, DEC);
	}
	Serial.print("-");
	Serial.print(RTC.year, DEC);
	Serial.println();
	delay(1000);
}

Now all that is left to do is upload the Arduino script. Determine which tty device the Arduino is and run the python script. Now your DS1307 is synced (within less than a second) for the next 5 years (battery dependent). If anyone has suggestions to make this better, I would love to hear them.

Update on the difference between the two clocks
As I mentioned earlier, I’m new to this. I was wondering how accurate the two times will be synced. I added some micros() before and after the data (see below). With this I had results floating around 4000 micro seconds (0.0004 seconds). I then checked out the time delay for the data to be transfered. Using a baud rate of 9600 it works out to be (19*8/9600) 0.016 seconds. I increased the baud rate in both scripts to 115200, and the new transfer time is (19*8/115200) 0.0013 seconds. So the DS1307 time should be within 0.002 seconds of the computers time. That’s good enough for me, I suppose if you want better than that you should get an ethernet shield.

if(Serial.available()==19&&TimeSet==0){
                timer1 = micros();
		for(int i=0;i<19;i++){
			dateIn += String(byte(Serial.read()));
			
		}
		RTC.stopClock();
		RTC.fillByYMD(getInt(dateIn.substring(0,4)),getInt(dateIn.substring(5,7)),getInt(dateIn.substring(8,10)));
		RTC.fillByHMS(getInt(dateIn.substring(11,13)),getInt(dateIn.substring(14,16)),getInt(dateIn.substring(17,19)));
		RTC.setTime();
		RTC.startClock();
                timer2 = micros()-timer1;

A mode filter for the maxbotix sensor

Carrying on from my last post I investigated how accurate the sonar readings were. Overall the readings are extremely stable (except when you don’t solider the pins in properly). To overcome the rare anomalies, and to make sure I will store the correct values further done the track I decided to come up with a mode filter which I couldn’t find on the arduino forums. I used the sorting function from this forum, googled around and came up with the following code. I also included a check to see if there is a mode, or if there are two modes, and if so, to grab the median value, instead of the mode.

/*
This script is designed to take several readings from the maxbotix sonar and generate a mode/median.
Author: Jason Lessels
Date created: 2011/June/06
Lisence: GPL (=>2)
This work has been compileed using many sources mainly posts/wiki posts from;
Allen, Bruce (2009/July/23) and Gentles, Bill (2010/Nov/12)
*/

//Set the pin to recieve the signal.
const int pwPin = 7;
//variables needed to store values
int arraysize = 9;  //quantity of values to find the median (sample size). Needs to be an odd number

//declare an array to store the samples. not necessary to zero the array values here, it just makes the code clearer
int rangevalue[] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0};
long pulse;
int modE;

void setup()
{

  //Open up a serial connection
  Serial.begin(9600);
  //Wait for the serial connection
  delay(500);

}
//Main loop where the action takes place
void loop()
{
  pinMode(pwPin, INPUT);

  for(int i = 0; i < arraysize; i++)
  {

    pulse = pulseIn(pwPin, HIGH);
    rangevalue[i] = pulse/58;
    delay(10);
  }

  Serial.print("Unsorted: ");
  printArray(rangevalue,arraysize);
  isort(rangevalue,arraysize);
  Serial.print("Sorted: ");
  printArray(rangevalue,arraysize);
  modE = mode(rangevalue,arraysize);
  Serial.print("The mode/median is: ");
  Serial.print(modE);
  Serial.println();
  delay(1000);
}

/*-----------Functions------------*/
//Function to print the arrays.
void printArray(int *a, int n)
{

  for (int i = 0; i < n; i++)
  {
    Serial.print(a[i], DEC);
    Serial.print(' ');
  }

  Serial.println();
}

//Sorting function
// sort function (Author: Bill Gentles, Nov. 12, 2010)
void isort(int *a, int n)
//  *a is an array pointer function
{
  for (int i = 1; i < n; ++i)   {     int j = a[i];     int k;     for (k = i - 1; (k >= 0) && (j < a[k]); k--)
    {
      a[k + 1] = a[k];
    }
    a[k + 1] = j;
  }
}

//Mode function, returning the mode or median.
int mode(int *x,int n){
  int i = 0;
  int count = 0;
  int maxCount = 0;
  int mode = 0;
  int bimodal;
  int prevCount = 0;

  while(iprevCount&count>maxCount){
      mode=x[i];
      maxCount=count;
      bimodal=0;
    }
    if(count==0){
      i++;
    }
    if(count==maxCount){//If the dataset has 2 or more modes.
      bimodal=1;
    }
    if(mode==0||bimodal==1){//Return the median if there is no mode.
      mode=x[(n/2)];
    }
    return mode;
  }
}

By the next post I hope to have a video of the setup of both the mode filter and the data storage in action. I have this working, but no time to get it into a video.

First play with the matbotix sonar sensor.

Today, I finally got to the local electric store before they had closed. I had to get a soldiering iron and some pcb headers. I soldiered the headers to the sonar sensor, and plugged it into my breadboard. This setup is very nasty at the moment, but i wanted to know if this was going to work for me. I have been doing a little reading and found a few useful links. The code from the playground site; http://www.arduino.cc/playground/Main/MaxSonar

Info from a matbotix guy (and filtering).
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1288290319

The code i modified and used for my MB7060:

//Feel free to use this code.
//Please be respectful by acknowledging the author in the code if you use or modify it.
//Author: Bruce Allen
//Date: 23/07/09

//Digital pin 7 for reading in the pulse width from the MaxSonar device.
//This variable is a constant because the pin will not change throughout execution of this code.
const int pwPin = 7; 
//variables needed to store values
long pulse, cm;
void setup() {
  //This opens up a serial connection to shoot the results back to the PC console
  Serial.begin(9600);
}
void loop() {
  pinMode(pwPin, INPUT);

  pulse = pulseIn(pwPin, HIGH);
  cm = pulse/58;
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();
  delay(500);
}