Commit 537a7b60 authored by Mohammad Orabe's avatar Mohammad Orabe
Browse files

init commit

parents
Pipeline #26028 failed with stages
in 1 second
Stock Monitoring in Smart Factory Warehouse
Objective
A smart factory produces various products and these products are stored in the warehouse. In order to monitor how many products are stored in the warehouse, the easiest approach is to record each product and its container in a system, so that the added products can be tracked by this system. In this study, our focus is to figure out how many products are available in a container without recording them in the system but by utilizing the load sensors instead. Basically, the load sensor measures the total load once a product is added in the container. Assuming that all products are the same in terms of the weight and form, we will be able to detect the number of the products in the container in the end of this study.
1. HX711 Sensor Implementation:
The integration of HX711 sensor can be performed with the following approaches. The code integration code relies main on two parts. The first describes the HX711 sensor with a class and this code communicates with the hardware. In the second part, we see the execution of this class, in other words, a simple test-case to understand whether the weight sensor functions.
2. Involving WebSocket Communication Protocol:
The aim of this task is to convey the measured data from the sensor to a web application, which will be presented in the next section. Websocket establishes two communications between two entities, however, only one direction from the sensor to the web-app will be implemented. This task has two folds: (1) Implementing the websocket communication on the sensor side, (2) Implementing the web socket communication on the web side.
3. Visualizing Data via Web Application:
The visualization of the incoming data is implemented with a simple index.html file, in which the socket communication is written. The index.html file includes all required libraries to be able to establish the socket communication, whereas HTML file reflects what is seen when the 127.0.0.1:10001 URL address is called on the web browser. .js file is responsible for receiving data from the connected channel and then update the HTML item when a new data is available.
Visit http://localhost:5000 to see the updating numbers.
import json
from flask_socketio import SocketIO, emit
from flask import Flask, render_template, url_for, copy_current_request_context
from time import sleep
from threading import Thread, Event
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
app.config['DEBUG'] = True
#turn the flask app into a socketio app
socketio = SocketIO(app, async_mode=None, logger=True, engineio_logger=True)
thread = Thread()
thread_stop_event = Event()
EMULATE_HX711 = True
if not EMULATE_HX711:
import RPi.GPIO as GPIO
from hx711 import HX711
else:
from emulated_hx711 import HX711
def get_data():
hx = HX711(5, 6)
hx.set_reading_format("MSB", "MSB")
hx.set_reference_unit(308)
hx.reset()
hx.tare()
print("Tare done! Add weight now...")
num_of_objects = 0
last_value = 0
while not thread_stop_event.isSet():
val = hx.get_weight(5)
if val > last_value:
if 30 <= abs(val - last_value) <= 75:
# print "Difference: %d" % (val - last_value)
num_of_objects += 1
last_value = val
elif last_value > val:
if 30 <= abs(last_value - val):
num_of_objects = max(0, num_of_objects - 1)
last_value = val
print("Sensors values: %s" % val)
print("Number of objects: %s" % num_of_objects)
print("___________________________________")
hx.power_down()
hx.power_up()
number = str(val) + ',' + str(num_of_objects)
socketio.emit('newnumber', {'number': number}, namespace='/test')
socketio.sleep(5)
@app.route('/')
def index():
#only by sending this page first will the client be connected to the socketio instance
return render_template('index.html')
@socketio.on('connect', namespace='/test')
def test_connect():
# need visibility of the global thread object
global thread
print('Client connected')
#Start the random number generator thread only if the thread has not been started before.
if not thread.isAlive():
print("Starting Thread")
thread = socketio.start_background_task(get_data)
@socketio.on('disconnect', namespace='/test')
def test_disconnect():
print('Client disconnected')
if __name__ == '__main__':
socketio.run(app)
\ No newline at end of file
import time
import random
import math
import threading
from numpy import long
class HX711:
def __init__(self, dout, pd_sck, gain=128):
self.PD_SCK = pd_sck
self.DOUT = dout
# Last time we've been read.
self.lastReadTime = time.time()
self.sampleRateHz = 80.0
self.resetTimeStamp = time.time()
self.sampleCount = 0
self.simulateTare = False
# Mutex for reading from the HX711, in case multiple threads in client
# software try to access get values from the class at the same time.
self.readLock = threading.Lock()
self.GAIN = 0
self.REFERENCE_UNIT = 1 # The value returned by the hx711 that corresponds to your reference unit AFTER dividing by the SCALE.
self.OFFSET = 1
self.lastVal = long(0)
self.DEBUG_PRINTING = False
self.byte_format = 'MSB'
self.bit_format = 'MSB'
self.set_gain(gain)
# Think about whether this is necessary.
time.sleep(1)
def convertToTwosComplement24bit(self, inputValue):
# HX711 has saturating logic.
if inputValue >= 0x7fffff:
return 0x7fffff
# If it's a positive value, just return it, masked with our max value.
if inputValue >= 0:
return inputValue & 0x7fffff
if inputValue < 0:
# HX711 has saturating logic.
if inputValue < -0x800000:
inputValue = -0x800000
diff = inputValue + 0x800000
return 0x800000 + diff
def convertFromTwosComplement24bit(self, inputValue):
return -(inputValue & 0x800000) + (inputValue & 0x7fffff)
def is_ready(self):
# Calculate how long we should be waiting between samples, given the
# sample rate.
sampleDelaySeconds = 1.0 / self.sampleRateHz
return time.time() >= self.lastReadTime + sampleDelaySeconds
def set_gain(self, gain):
if gain is 128:
self.GAIN = 1
elif gain is 64:
self.GAIN = 3
elif gain is 32:
self.GAIN = 2
# Read out a set of raw bytes and throw it away.
self.readRawBytes()
def get_gain(self):
if self.GAIN == 1:
return 128
if self.GAIN == 3:
return 64
if self.GAIN == 2:
return 32
# Shouldn't get here.
return 0
def readRawBytes(self):
# Wait for and get the Read Lock, incase another thread is already
# driving the virtual HX711 serial interface.
self.readLock.acquire()
# Wait until HX711 is ready for us to read a sample.
while not self.is_ready():
pass
self.lastReadTime = time.time()
# Generate a 24bit 2s complement sample for the virtual HX711.
rawSample = self.convertToTwosComplement24bit(self.generateFakeSample())
# Read three bytes of data from the HX711.
firstByte = (rawSample >> 16) & 0xFF
secondByte = (rawSample >> 8) & 0xFF
thirdByte = rawSample & 0xFF
# Release the Read Lock, now that we've finished driving the virtual HX711
# serial interface.
self.readLock.release()
# Depending on how we're configured, return an orderd list of raw byte
# values.
if self.byte_format == 'LSB':
return [thirdByte, secondByte, firstByte]
else:
return [firstByte, secondByte, thirdByte]
def read_long(self):
# Get a sample from the HX711 in the form of raw bytes.
dataBytes = self.readRawBytes()
if self.DEBUG_PRINTING:
print (dataBytes)
# Join the raw bytes into a single 24bit 2s complement value.
twosComplementValue = ((dataBytes[0] << 16) |
(dataBytes[1] << 8) |
dataBytes[2])
if self.DEBUG_PRINTING:
print ("Twos: 0x%06x" % twosComplementValue)
# Convert from 24bit twos-complement to a signed value.
signedIntValue = self.convertFromTwosComplement24bit(twosComplementValue)
# Record the latest sample value we've read.
self.lastVal = signedIntValue
# Return the sample value we've read from the HX711.
return long(signedIntValue)
def read_average(self, times=3):
# Make sure we've been asked to take a rational amount of samples.
if times <= 0:
print ("HX711().read_average(): times must >= 1!! Assuming value of 1.")
times = 1
# If we're only average across one value, just read it and return it.
if times == 1:
return self.read_long()
# If we're averaging across a low amount of values, just take an
# arithmetic mean.
if times < 5:
values = long(0)
for i in range(times):
values += self.read_long()
return values / times
# If we're taking a lot of samples, we'll collect them in a list, remove
# the outliers, then take the mean of the remaining set.
valueList = []
for x in range(times):
valueList += [self.read_long()]
valueList.sort()
# We'll be trimming 20% of outlier samples from top and bottom of collected set.
trimAmount = int(len(valueList) * 0.2)
# Trim the edge case values.
valueList = valueList[trimAmount:-trimAmount]
# Return the mean of remaining samples.
return sum(valueList) / len(valueList)
def get_value(self, times=3):
return self.read_average(times) - self.OFFSET
def get_weight(self, times=3):
value = self.get_value(times)
value = value / self.REFERENCE_UNIT
return value
def tare(self, times=15):
# If we aren't simulating Taring because it takes too long, just skip it.
if not self.simulateTare:
return 0
# Backup REFERENCE_UNIT value
reference_unit = self.REFERENCE_UNIT
self.set_reference_unit(1)
value = self.read_average(times)
if self.DEBUG_PRINTING:
print ("Tare value:", value)
self.set_offset(value)
# Restore the reference unit, now that we've got our offset.
self.set_reference_unit(reference_unit)
return value
def set_reading_format(self, byte_format="LSB", bit_format="MSB"):
if byte_format == "LSB":
self.byte_format = byte_format
elif byte_format == "MSB":
self.byte_format = byte_format
else:
print ("Unrecognised byte_format: \"%s\"" % byte_format)
if bit_format == "LSB":
self.bit_format = bit_format
elif bit_format == "MSB":
self.bit_format = bit_format
else:
print ("Unrecognised bit_format: \"%s\"" % bit_format)
def set_offset(self, offset):
self.OFFSET = offset
def get_offset(self):
return self.OFFSET
def set_reference_unit(self, reference_unit):
# Make sure we aren't asked to use an invalid reference unit.
if reference_unit == 0:
print ("HX711().set_reference_unit(): Can't use 0 as a reference unit!!")
return
self.REFERENCE_UNIT = reference_unit
def power_down(self):
# Wait for and get the Read Lock, incase another thread is already
# driving the HX711 serial interface.
self.readLock.acquire()
# Wait 100us for the virtual HX711 to power down.
time.sleep(0.0001)
# Release the Read Lock, now that we've finished driving the HX711
# serial interface.
self.readLock.release()
def power_up(self):
# Wait for and get the Read Lock, incase another thread is already
# driving the HX711 serial interface.
self.readLock.acquire()
# Wait 100 us for the virtual HX711 to power back up.
time.sleep(0.0001)
# Release the Read Lock, now that we've finished driving the HX711
# serial interface.
self.readLock.release()
# HX711 will now be defaulted to Channel A with gain of 128. If this
# isn't what client software has requested from us, take a sample and
# throw it away, so that next sample from the HX711 will be from the
# correct channel/gain.
if self.get_gain() != 128:
self.readRawBytes()
def reset(self):
# self.power_down()
# self.power_up()
# Mark time when we were reset. We'll use this for sample generation.
self.resetTimeStamp = time.time()
def generateFakeSample(self):
sampleTimeStamp = time.time() - self.resetTimeStamp
noiseScale = 1.0
noiseValue = random.randrange(-(noiseScale * 1000),(noiseScale * 1000)) / 1000.0
sample = math.sin(math.radians(sampleTimeStamp * 20)) * 72.0
self.sampleCount += 1
if sample < 0.0:
sample = -sample
sample += noiseValue
BIG_ERROR_SAMPLE_FREQUENCY = 142
###BIG_ERROR_SAMPLE_FREQUENCY = 15
BIG_ERROR_SAMPLES = [0.0, 40.0, 70.0, 150.0, 280.0, 580.0]
if random.randrange(0, BIG_ERROR_SAMPLE_FREQUENCY) == 0:
sample = random.sample(BIG_ERROR_SAMPLES, 1)[0]
print ("Sample %d: Injecting %f as a random bad sample." % (self.sampleCount, sample))
sample *= 1000
sample *= self.REFERENCE_UNIT
return int(sample)
# EOF - emulated_hx711.py
#
import RPi.GPIO as GPIO
import time
import threading
from numpy import long
class HX711:
def __init__(self, dout, pd_sck, gain=128):
self.PD_SCK = pd_sck
self.DOUT = dout
# Mutex for reading from the HX711, in case multiple threads in client
# software try to access get values from the class at the same time.
self.readLock = threading.Lock()
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.PD_SCK, GPIO.OUT)
GPIO.setup(self.DOUT, GPIO.IN)
self.GAIN = 0
# The value returned by the hx711 that corresponds to your reference
# unit AFTER dividing by the SCALE.
self.REFERENCE_UNIT = 1
self.REFERENCE_UNIT_B = 1
self.OFFSET = 1
self.OFFSET_B = 1
self.lastVal = long(0)
self.DEBUG_PRINTING = False
self.byte_format = 'MSB'
self.bit_format = 'MSB'
self.set_gain(gain)
# Think about whether this is necessary.
# time.sleep(1)
def convertFromTwosComplement24bit(self, inputValue):
return -(inputValue & 0x800000) + (inputValue & 0x7fffff)
def is_ready(self):
return GPIO.input(self.DOUT) == 0
def set_gain(self, gain):
if gain is 128:
self.GAIN = 1
elif gain is 64:
self.GAIN = 3
elif gain is 32:
self.GAIN = 2
GPIO.output(self.PD_SCK, False)
# Read out a set of raw bytes and throw it away.
self.readRawBytes()
def get_gain(self):
if self.GAIN == 1:
return 128
if self.GAIN == 3:
return 64
if self.GAIN == 2:
return 32
# Shouldn't get here.
return 0
def readNextBit(self):
# Clock HX711 Digital Serial Clock (PD_SCK). DOUT will be
# ready 1us after PD_SCK rising edge, so we sample after
# lowering PD_SCL, when we know DOUT will be stable.
GPIO.output(self.PD_SCK, True)
GPIO.output(self.PD_SCK, False)
value = GPIO.input(self.DOUT)
# Convert Boolean to int and return it.
return int(value)
def readNextByte(self):
byteValue = 0
# Read bits and build the byte from top, or bottom, depending
# on whether we are in MSB or LSB bit mode.
for x in range(8):
if self.bit_format == 'MSB':
byteValue <<= 1
byteValue |= self.readNextBit()
else:
byteValue >>= 1
byteValue |= self.readNextBit() * 0x80
# Return the packed byte.
return byteValue
def readRawBytes(self):
# Wait for and get the Read Lock, incase another thread is already
# driving the HX711 serial interface.
self.readLock.acquire()
# Wait until HX711 is ready for us to read a sample.
while not self.is_ready():
pass
# Read three bytes of data from the HX711.
firstByte = self.readNextByte()
secondByte = self.readNextByte()
thirdByte = self.readNextByte()
# HX711 Channel and gain factor are set by number of bits read
# after 24 data bits.
for i in range(self.GAIN):
# Clock a bit out of the HX711 and throw it away.
self.readNextBit()
# Release the Read Lock, now that we've finished driving the HX711
# serial interface.
self.readLock.release()
# Depending on how we're configured, return an orderd list of raw byte
# values.
if self.byte_format == 'LSB':
return [thirdByte, secondByte, firstByte]
else:
return [firstByte, secondByte, thirdByte]
def read_long(self):
# Get a sample from the HX711 in the form of raw bytes.
dataBytes = self.readRawBytes()
if self.DEBUG_PRINTING:
print (dataBytes)
# Join the raw bytes into a single 24bit 2s complement value.
twosComplementValue = ((dataBytes[0] << 16) |
(dataBytes[1] << 8) |
dataBytes[2])
if self.DEBUG_PRINTING:
print ("Twos: 0x%06x" % twosComplementValue)
# Convert from 24bit twos-complement to a signed value.
signedIntValue = self.convertFromTwosComplement24bit(twosComplementValue)
# Record the latest sample value we've read.
self.lastVal = signedIntValue
# Return the sample value we've read from the HX711.
return long(signedIntValue)
def read_average(self, times=3):
# Make sure we've been asked to take a rational amount of samples.
if times <= 0:
raise ValueError("HX711()::read_average(): times must >= 1!!")
# If we're only average across one value, just read it and return it.
if times == 1:
return self.read_long()
# If we're averaging across a low amount of values, just take the
# median.
if times < 5:
return self.read_median(times)
# If we're taking a lot of samples, we'll collect them in a list, remove
# the outliers, then take the mean of the remaining set.
valueList = []
for x in range(times):