Feed on
Posts
Comments

ATmega GPS SMS Alerter

Previously I made a ATtiny GPS Long Lat logger to record the GPS co-ordinates to an EEPROM and now it’s time to implement the full functionality of my project which is to check an accelerometer for motion, monitor the GPS until we are in a certain area and then send an SMS. If it’s a few KM away from the area, then it’ll turn the GPS off for a few minutes and check again later but if it’s close to the area, it will keep checking the GPS as long as movement is detected within 1 minute.

https://www.youtube.com/watch?v=JNAqu732Uqw

I was using the ATtiny2313 before however it didn’t have any ADCs so I’ve switched to the ATmega88 which is approximately the same price. To reduce power I have a mosfet for the accelerometer, GPS and mobile phone. The mobile I’m using this time is the Nokia 3210 which cost me $22 from Ebay, the battery seemed to discharge after a couple of days so I was trying out different options.

//  Reset  PC6  1|o   |28  PC5 N Mosfet for MMA7361 accelerometer
//  RX GPS PD0  2|    |27  PC4 N Mosfet for GPS
//  TX SMS PD1  3|    |26  PC3 N Mosfet for Nokia 3120
//         PD2  4|    |25  PC2 X Axis
//         PD3  5|    |24  PC1 Y Axis
//         PD4  6|    |23  PC0 Z Axis
//         VCC  7|    |22  GND
//         GND  8|    |21  AREF
//    XTAL PB6  9|    |20  AVCC
//    XTAL PB7 10|    |19  PB5 SCK
//         PD5 11|    |18  PB4 MISO
//         PD6 12|    |17  PB3 MOSI
//         PD7 13|    |16  PB2 Optoisolator for Nokia 3120 power button
//         PB0 14|    |15  PB1 LED

The Nokia 3210 seems to power up from ~2.3V to 3.4V, so one option is to try a 3.7V Li-ion with a diode to drop it however after some experimentation it switches only stays on for a few seconds.

gsa-2

I thought it would be interesting to try out a super capacitor, the first one I purchased I didn’t notice the internal resistance which was quite high at 75 ohms so it didn’t work out as the phone didn’t switch on at all however these types of super capacitors are good for low current draw as a few days later the capacitor maintained it’s voltage.

gsa-1

The next capacitor was the film type, after charging it to 2.5V it seemed to power up the phone but only for 3 seconds, I tried to use the diode solution with the super capacitor (2 in series), but it still switched off after a few seconds. Sometimes the easiest solution is the best one, just a pair of AA batteries powers up the phone just fine but in the future I’m looking at grabbing a adjustable LDO regulator and seeing how well that will run from the 3.7V Li-poly.

gsa-3

Here’s how the schematic looks.

// Convert the GPS NMEA to decimal (without using atof as it takes up too much space)
double gps_nmea_to_double(uint8_t* gpsData) {
	// Read until decimal place
	int x = 0;
	while (gpsData[x] != '.') {
		x++;
	}

	// Take the minutes from the part before the decimal point
	char tempint[5];
	memcpy(tempint, gpsData+(x-2), 2);
	tempint[3] = 0;
	int minutes = atoi((char*) &tempint); 

	// Take the degrees from the part before the decimal point
	memcpy(tempint, gpsData, (x-2));
	tempint[(x-2)] = 0;
	int degrees = atoi((char*) &tempint); 

	// Convert the part after the decimal point to an int
	uint32_t afterDecimalpoint = atol((char*) &gpsData[++x]); 

	// Now do the NMEA to decimal conversion
	double calculateDecimal = (double) (((double) minutes + (double) afterDecimalpoint / (double) 100000) / (double) 60) + degrees;

	return calculateDecimal;
}

Now on to the code, firstly I was able to make a function without using atof to convert the GPS Long and Lat co-ordinates to a number. We read the number to the decimal point, break up the number before and after the decimal point and then combine them together.

void calculate_shortest_distance(double compare, double compared_to) {
	int distance = (double) (compare * 100) - (double) (compared_to * 100);
	if (distance < 0) {
		distance = distance - (distance * 2);
	}
	if (distance < gpsLowestDistance) {
		gpsLowestDistance = distance;
	}
}

Next is another function that I use to calculate the distance away between two GPS co-ordinates and assigns the shortest difference to the gpsLowestDistance global varaiable. After checking on Google maps it seems that 1KM is approximately 0.01 in longitude or latitude so we times by 100 to give us a round number for each KM (a result of 1 means there is a difference of 1KM)

uint8_t check_for_movement(uint8_t time_to_monitor) {
	// Turn on accelerometer and ADC
	PORTC |= (1<<MMA_MOSFET);
	sbi(ADCSRA, ADEN);
	adc_read(X_AXIS); // Ignore first reading
	watchdog_sleep(T64MS);

	// Measure if motion is detected over 10 seconds
	int xResult = adc_read(X_AXIS);
	int yResult = adc_read(Y_AXIS);
	int zResult = adc_read(Z_AXIS);

	// Read X, Y, Z axis every 250ms
	int highestValue = 0;
	for (int i = 0; i < (time_to_monitor * 4); i++) {
		int xChange = adc_read(X_AXIS) - xResult;
		int yChange = adc_read(Y_AXIS) - yResult;
		int zChange = adc_read(Z_AXIS) - zResult;

		// Convert negative to positive value
		if (xChange < 0) {
			xChange = xChange - (xChange * 2);
		}
		if (yChange < 0) {
			yChange = yChange - (yChange * 2);
		}
		if (zChange < 0) {
			zChange = zChange - (zChange * 2);
		}

		// Record which one had the highest change
		if (xChange > highestValue) {
			highestValue = xChange;
		}
		if (yChange > highestValue) {
			highestValue = yChange;
		}
		if (zChange > highestValue) {
			highestValue = zChange;
		}

		watchdog_sleep(T250MS);

		// Movement detected? 0.1g is 80mV assuming 800mV/g typical.
		if (highestValue >= 200) {
			// Turn off accelerometer and ADC
			PORTC &= ~(1<<MMA_MOSFET);
			cbi(ADCSRA, ADEN);

			return 1;
		}
	}

	// Turn off accelerometer and ADC
	PORTC &= ~(1<<MMA_MOSFET);
	cbi(ADCSRA, ADEN);

	return 0;
}

Firstly when starting up, we check if the accelerometer detects any movement for 10 seconds on all axises and if so, turn on the GPS otherwise check again in 1 minute.

// In SMS alert area
if (gpsLatitudeDec > compareLatitudeUp && gpsLatitudeDec < compareLatitudeDown && 
	gpsLongitudeDec > compareLongitudeLeft && gpsLongitudeDec < compareLongitudeRight) {

	// Set re-check timer to 1 hour
	eightSecondCounter = 450;

	// Turn off GPS
	PORTC &= ~(1<<GPS_MOSFET);
	monitorGPS = false;

	// Disable RX
	cbi(UCSR0B, RXEN0); // Receiver disable

	// Enable TX
	UBRR0L = 12; // 115,200 Baud rate
	sbi(UCSR0A, U2X0); // Double rate
	sbi(UCSR0B, TXEN0); // Transmitter enable

	// Provide power to the phone
	PORTC |= (1<<NOKIA_MOSFET);

	// Wait a second and then hold the power button down to start the phone
	watchdog_sleep(T1S);
	PORTB |= (1<<NOKIA_POWER_BUTTON);
	watchdog_sleep(T2S);
	PORTB &= ~(1<<NOKIA_POWER_BUTTON);

	// Wait for phone to boot up
	watchdog_sleep(T8S);
	watchdog_sleep(T8S);

	// Transmit U 128 times
	int x;
	for (x = 0; x < 128; x++) {
		USART_Transmit('U'); 
	}
	_delay_ms(50);

	// Send SMS
	SendSMS("In area");
	_delay_ms(50);

	// Wait a few seconds and then turn off phone
	watchdog_sleep(T8S);
	PORTC &= ~(1<<NOKIA_MOSFET);

	// Disable TX
	cbi(UCSR0B, TXEN0);

	// Enable RX
	UBRR0L = 155; // 9,600 Baud rate
	sbi(UCSR0A, U2X0); // Double rate
	sbi(UCSR0B, RXEN0); // Receiver enable

}
else {
	PORTB &= ~(1<<PB1);

	// How far are we away from alert area? Find the shortest distance of the Latitude and Longitude.
	gpsLowestDistance = 100;
	calculate_shortest_distance(compareLatitudeUp, gpsLatitudeDec);
	calculate_shortest_distance(compareLatitudeDown, gpsLatitudeDec);
	calculate_shortest_distance(compareLongitudeLeft, gpsLongitudeDec);
	calculate_shortest_distance(compareLongitudeRight, gpsLongitudeDec);

	// If more than 5KM away, check again in 5 minutes
	if (gpsLowestDistance > 5) {
		// Turn off GPS
		PORTC &= ~(1<<GPS_MOSFET);
		monitorGPS = false;

		eightSecondCounter = 38; // Check again in 5 minutes
	}
	else { // Less than 5KM away
		if (check_for_movement(60) == false) { // If we aren't moving over 1 minute, turn off GPS
			// Turn off GPS
			PORTC &= ~(1<<GPS_MOSFET);
			monitorGPS = false;

			eightSecondCounter = 8; // Check again in 1 minute
		}
	}

}
...
// Sleep until we reach 0
while (eightSecondCounter >= 1) {
	watchdog_sleep(T8S);
	eightSecondCounter--;
}

After switching the GPS on and grabbing the Long and Lat, if we find we’re in the SMS alert area, we set our re-check timer to 1 hour so that if movement is detected again we don’t send another duplicate SMS. We then disable the GPS, USART RX, set up the next baud rate for the Nokia and enable the TX, power up the phone and send our SMS message.

Now if we’re not in the SMS alert area, we calculate how far we are away from the alert’s Long/Lat markers and find the shortest distance. If we detect that we’re more than 5KM away, we can turn off the GPS for about 5 minutes to save power. If we are within 5KM, we continually check if there is movement within 60 seconds, if there is we check the GPS but if there isn’t we turn off the GPS for 1 minute.

Download ATmega_GPS_SMS_Alerter_v0.1

And that’s all there is to it, seems fairly simple once you have it all up and running.

Leave a Reply