Skip to content

Part 5. The Code

Elizabeth Adams edited this page Nov 26, 2018 · 6 revisions

Now we're to the fun part. You can either copy the fonagps.py script over to your Pi manually, or you can clone it directly, but you'll want to plug in a WiFi dongle for either option.

Let's give the script a brief look over.

Lines 7-10 set your bucket name, bucket key, access key, and how long you wait between GPS readings. Remember, your Initial State access key can be found on your account page. The bucket key is the truly unique identifier for your bucket, so remember what it is in case your bucket fails to be created later.

BUCKET_NAME = "Fona GPS"
BUCKET_KEY = "fona"
ACCESS_KEY = "Your_Access_Key"
SECONDS_BETWEEN_READS = 60

The openPPPD() function works by checking if a cellular connection has already been established and then turning PPPD on if it hasn't.

# Start PPPD
def openPPPD():
	# Check if PPPD is already running by looking at syslog output
	output1 = subprocess.check_output("cat /var/log/syslog | grep pppd | tail -1", shell=True)
	if "secondary DNS address" not in output1 and "locked" not in output1:
		while True:
			# Start the "fona" process
			subprocess.call("sudo pon fona", shell=True)
			sleep(2)
			output2 = subprocess.check_output("cat /var/log/syslog | grep pppd | tail -1", shell=True)
			if "script failed" not in output2:
				break
	# Make sure the connection is working
	while True:
		output2 = subprocess.check_output("cat /var/log/syslog | grep pppd | tail -1", shell=True)
		output3 = subprocess.check_output("cat /var/log/syslog | grep pppd | tail -3", shell=True)
		if "secondary DNS address" in output2 or "secondary DNS address" in output3:
			return True

This function sends the "sudo poff fona" command.

# Stop PPPD
def closePPPD():
	print "turning off cell connection"
	# Stop the "fona" process
	subprocess.call("sudo poff fona", shell=True)
	# Make sure connection was actually terminated
	while True:
		output = subprocess.check_output("cat /var/log/syslog | grep pppd | tail -1", shell=True)
		if "Exit" in output:
			return True

The checkForFix() function starts a serial connection so that we can send AT commands to the Fona (the same ones we used earlier in the screen environment) and turns on the GPS.

# Check for a GPS fix
def checkForFix():
	print "checking for fix"
	# Start the serial connection
	ser=serial.Serial('/dev/ttyAMA0', 115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1)
	# Turn on the GPS
	ser.write("AT+CGNSPWR=1\r")
	ser.write("AT+CGNSPWR?\r")
	while True:
		response = ser.readline()
		if " 1" in response:
			break
	# Ask for the navigation info parsed from NMEA sentences
	ser.write("AT+CGNSINF\r")
	while True:
			response = ser.readline()
			# Check if a fix was found
			if "+CGNSINF: 1,1," in response:
				print "fix found"
				print response
				return True
			# If a fix wasn't found, wait and try again
			if "+CGNSINF: 1,0," in response:
				sleep(5)
				ser.write("AT+CGNSINF\r")
				print "still looking for fix"
			else:
				ser.write("AT+CGNSINF\r")

The getCoord() function could be used to fetch any of the GPS data, but, in this case, is used to get latitude and longitude by indexing them from the CGNSINF output.

# Read the GPS data for Latitude and Longitude
def getCoord():
	# Start the serial connection
	ser=serial.Serial('/dev/ttyAMA0', 115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1)
	ser.write("AT+CGNSINF\r")
	while True:
		response = ser.readline()
		if "+CGNSINF: 1," in response:
			# Split the reading by commas and return the parts referencing lat and long
			array = response.split(",")
			lat = array[3]
			print lat
			lon = array[4]
			print lon
			return (lat,lon)

Here we open the cellular connection and create a bucket in Initial State to hold our data. The 10 second sleep is so that there is enough time to send the request before we cut the connection. The buffer_size setting when we initialize the streamer is how many data points the streamer will collect before actually sending them to Initial State. We manually control this later in the code, but this is a bit of a safety net to keep the streamer from trying to send data before we want it to (so the buffer_size is set to 20 even though we're sending every 10 data points).

# Start the program by opening the cellular connection and creating a bucket for our data
if openPPPD():
	# Initialize the Initial State streamer
	streamer = Streamer(bucket_name=BUCKET_NAME, bucket_key=BUCKET_KEY, access_key=ACCESS_KEY, buffer_size=20)
	# Wait long enough for the request to complete
	sleep(10)

Finally, we close the connection, take GPS readings, open the connection, and then stream them.

	while True:
		# Close the cellular connection
		if closePPPD():
			print "closing connection"
			sleep(1)
		# The range is how many data points we'll collect before streaming
		for i in range(10):
			# Make sure there's a GPS fix
			if checkForFix():
				# Get lat and long
				if getCoord():
					latitude, longitude = getCoord()
					coord = str(latitude) + "," + str(longitude)
					print coord
					# Buffer the coordinates to be streamed
					streamer.log("Coordinates",coord)
					sleep(SECONDS_BETWEEN_READS)
			# Turn the cellular connection on every 10 reads
			if i == 9:
				print "opening connection"

				if openPPPD():
					print "streaming"
					# Flush the streaming queue and send the data
					streamer.flush()
					print "streaming complete"

Even though we have a streamer.log statement before the internet connection is open, the Initial State python streaming module is storing data locally before uploading it in a batch of 10. The data is being stored on line 110 where the "streamer.log()" statement is and officially uploaded on line 119 when the streamer is flushed with streamer.flush().

The batch number can changed by changing the range on line 101 and editing line 113 to be 1 less than the range (be sure to update the buffer_size on line 91 if batching more than 20 data points).

You could easily accomplish this a different way by using an array to store the data instead and then uploading that - just put the new code where the streamer statements are.

Now save and run the code!

Note: If you want to test this and know that you won't get a GPS fix, you can change line 59 from if "+CGNSINF: 1,1," in response: to if "+CGNSINF: 1,0," in response: and swap line 64 from if "+CGNSINF: 1,0," in response: to if "+CGNSINF: 1,1," in response:. This will make it run without a fix. But you also need to actually stream data for it to show up in Initial State (and lat/long will be blank with no fix), so change the indices on lines 81 and 83 to [1] and [2] to fetch (irrelevant) numbers and stream them.

Extra Note: If for some reason your bucket doesn't show up and you're sure everything's running correctly, it might just be that bucket creation failed. You can expose your bucket using the Initial State interface - read how here - just be sure to use the same bucket key that's in your script!

<< Part 5: Initial State - Part 5: Your Personal Dashboard >>