Joachim Kristensen
Published © GPL3+

Knock Knock, Who's Sending To Sigfox

A simple system using the Adafruit ADXL345 along with the PyCom SiPy to detect knocks, accelerations and other movement.

BeginnerProtip1 hour1,238
Knock Knock, Who's Sending To Sigfox

Things used in this project

Hardware components

SiPy
Pycom SiPy
×1
Adafruit ADXL345
×1
Pycom External Antenna
×1

Software apps and online services

Pymakr Plugin
Pycom Pymakr Plugin
Sublime Text 2

Hand tools and fabrication machines

Hakko 936

Story

Read more

Code

ADXL345 driver for Pycom SiPy

MicroPython
This is basically just a driver for the Adafruit ADXL345 accelerometer. In the text I have written, in the comments, what I think there could be improved on the driver code.
###
### Made with inspiration from: https://github.com/pimoroni/adxl345-python/blob/master/adxl345.py
### This is an I2C driver for the Adafruit ADXL345 Accelerometer: https://learn.adafruit.com/adxl345-digital-accelerometer?view=all
### At the moment it is possible to set the data range going from 2G to 16G
### Optimizations that could be done:
### - Write the binaries for the other output data rates
### - Write a calibration part
### - Make it possible to call the initiate the changes to data range and
### bandwidth from main program



#The address of the ADXL345 given in the datasheet
ADXL345_ADDR = 0x53


#The bytes for making the ADXL345 send at 100Hz output data rate
BW_RATE_100HZ = 0x0B

#The address for making changes to POWER_CTL
POWER_CTL = 0x2D 
#The byte "code" for starting the measurements
MEASURE = 0x08

#The address for changing the DATA_FORMAT. This is used together with the ranges
DATA_FORMAT = 0x31

#The address where the measurement data starts from. Each axis has two bytes for the given value
AXES_DATA = 0x32

#The address for accessing and setting the bandwidth rate
BW_RATE = 0x2C

#Decide the range of measurements ie the precision. Possible options
#2G
RANGE_2G = 0x08
#4G
RANGE_4G = 0x09
#8G
RANGE_8G = 0x2A
#16G
RANGE_16G = 0x0F

SCALE_MULTIPLIER = 0.004

#Standard gravity constant for going from G-force to m/s^2
EARTH_GRAVITY_MS2 = 9.80665



class ADXL345:

	def __init__(self, i2c):
		self.i2c = i2c
		self.addr = ADXL345_ADDR
		self.setBandwidthRate(BW_RATE_100HZ)
		self.setRange(RANGE_8G)
		self.enableMeasurement()

	def enableMeasurement(self):
		self.i2c.writeto_mem(self.addr, POWER_CTL, bytes([MEASURE]))
		
	def setBandwidthRate(self, rate_flag):
		self.i2c.writeto_mem(self.addr, BW_RATE, bytes([rate_flag]))

	def setRange(self, range_flag):
		self.i2c.writeto_mem(self.addr, DATA_FORMAT, bytes([range_flag]))

	def getAxes(self, gforce = False):
		bytes = self.i2c.readfrom_mem(self.addr, AXES_DATA, 6)
		x = bytes[0] | (bytes[1] << 8)
		if(x & (1 << 16 - 1)):
			x = x - (1<<16)

		y = bytes[2] | (bytes[3] << 8)
		if(y & (1 << 16 - 1)):
			y = y - (1<<16)

		z = bytes[4] | (bytes[5] << 8 )
		if(z & (1 << 16 - 1)):
			z = z - (1<<16)

		x = x * SCALE_MULTIPLIER
		y = y * SCALE_MULTIPLIER
		z = z * SCALE_MULTIPLIER

		if gforce == False: 
			x = x * EARTH_GRAVITY_MS2
			y = y * EARTH_GRAVITY_MS2
			z = z * EARTH_GRAVITY_MS2

		x = round(x,4)
		y = round(y,4)
		z = round(z,4)

		return {"x": x, "y": y, "z": z}

Main.py for the project

MicroPython
The main program file for the project
import time
import machine
import pycom
import adxl345

pycom.heartbeat(False)

#Set the I2C and Pin to machine. so the code from before still works
I2C = machine.I2C
Pin = machine.Pin
Timer = machine.Timer

chrono = Timer.Chrono()

#initialize the I2C bus
i2c = I2C(0, I2C.MASTER, baudrate=100000)

value = 0

time.sleep_ms(1000)
setRTCLocalTime()

savedTime = time.localtime()
counter = 1
timeThreshold = 600

#threshold is measued in g
threshold = 1
stateMotion = False
hitCount = 0
stateSigfox = False
measureCounter = 500
measureThreshold = 500

#Set this to false to turn off indication lights
lightVar = True

if lightVar == True:
	print('Turn lights on')

chrono.start()
print('Starting the loop')

while True:
	data = adxl345.ADXL345(i2c)
	axes = data.getAxes(True)
	x = axes['x']
	x = abs(x)
	y = axes['y']
	y = abs(y)
	z = axes['z']
	z = abs(z)
	measureCounter += 1
	if (threshold <= x) or (threshold <= y) or (threshold <= z):
		if measureThreshold <= measureCounter:
			stateMotion = True
			hitCount = hitCount + 1
			print('I have been hitten')
			print('My count is ')
			print(hitCount)
			measureCounter = 0
	#This function checks if it is allowed to send a message via SigFox (one every 10 minutes)
	if  timeThreshold <= chrono.read():
		stateSigfox = True
		chrono.stop()
	#This function first checks if the state of motion has changed, ie. have the acc crossed the threshold
	if stateMotion == True:
		#Then it checks if it is allowed to send a message
		if stateSigfox == True:
			#Send shit to sigfox
			print('I am going to send this hit count ')
			print(hitCount)
			#Send the bitcount to SigFox
			hitCount = str(hitCount)
			s.send("Hit" + hitCount)
			counter = 1
			stateSigfox = False
			stateMotion = False
			hitCount = 0
			chrono.reset()
			chrono.start()
	if lightVar == True:
		if (stateMotion == True) and (stateSigfox != True):
			#There has been motion but SigFox is not allowed to send messages
			pycom.rgbled(0x007f00) #green
		elif (stateMotion != True) and (stateSigfox == True):
			#Sigfox can send message but there has been no motion
			pycom.rgbled(0x7f7f00) #yellow
		elif (stateMotion != True) and (stateSigfox != True):
			#Neither motion has occured or SigFox can send messages
			pycom.rgbled(0x7f0000) #red
			

Boot.py for the project

MicroPython
The boot.py file for the project
known_nets = [('ssid', 'pass')] 

import machine
import os
from network import Sigfox
import binascii
import socket
import time

#Initiates Sigfox communication
sigfox = Sigfox(mode=Sigfox.SIGFOX, rcz=Sigfox.RCZ1) #RCZ1/RCZ3 Europe / Japan / Korea

#initiates the UART (USB) connection
uart = machine.UART(0, 115200)
os.dupterm(uart)

#WiFi setup
if machine.reset_cause() != machine.SOFT_RESET: #needed to avoid losing connection after a soft reboot
	from network import WLAN
	wl = WLAN()

	# save the default ssid and auth
	original_ssid = wl.ssid()
	original_auth = wl.auth()

	wl.mode(WLAN.STA)

	available_nets = wl.scan()
	nets = frozenset([e.ssid for e in available_nets])

	known_nets_names = frozenset([e[0] for e in known_nets])
	net_to_use = list(nets & known_nets_names)

	try:
		net_to_use = net_to_use[0]
		pwd = dict(known_nets) [net_to_use]
		sec = [e.sec for e in available_nets if e.ssid == net_to_use][0]
		wl.connect(net_to_use, (sec, pwd), timeout=10000)
	except:
		wl.init(mode=WLAN.AP, ssid=original_ssid, auth=original_auth, channel=6, antenna=WLAN.INT_ANT)

#SigFox setup

#Create a Sigfox socket
s = socket.socket(socket.AF_SIGFOX, socket.SOCK_RAW)
#Make the socket blocking
s.setblocking(True)
#Configure it as uplink only
s.setsockopt(socket.SOL_SIGFOX, socket.SO_RX, False)

#Time setup
def setRTCLocalTime():
	rtc = machine.RTC()
	rtc.ntp_sync("pool.ntp.org")
	time.sleep_ms(750)
	print('\nRTC Set from NTP to UTC', rtc.now())
	time.timezone(3600) #GMT + 1 Copenhagen, Amsterdan, Paris
	print('Adjusted from UTC to GMT+1', time.localtime(), '\n')

Credits

Joachim Kristensen

Joachim Kristensen

1 project • 3 followers

Comments