Commit f648dfec authored by Guillaume Blanchy's avatar Guillaume Blanchy
Browse files

Merge branch 'updateGB' into 'master'

UI update

See merge request reversaal/OhmPi!10
Showing with 764 additions and 576 deletions
+764 -576
data/*.csv
data/*
**/.ipynb_notebooks/**
data.zip
__pycache__
Ohmpi_4elec_mqtt.py
ohmpy/*
logs/*
sequence.txt
......@@ -12,7 +12,8 @@ OHMPI_CONFIG = {
'integer': 2, # Max value 10 # TODO: Explain what this is...
'version': 2,
'max_elec': 64,
'board_address': {'A': 0x76, 'B': 0x71, 'M': 0x74, 'N': 0x70} # def. {'A': 0x76, 'B': 0x71, 'M': 0x74, 'N': 0x70}
'board_address': {'A': 0x70, 'B': 0x71, 'M': 0x72, 'N': 0x73} # def. {'A': 0x76, 'B': 0x71, 'M': 0x74, 'N': 0x70}
#'board_address': {'A': 0x76, 'B': 0x71, 'M': 0x74, 'N': 0x70} # def. {'A': 0x76, 'B': 0x71, 'M': 0x74, 'N': 0x70}
}
# Execution logging configuration
......@@ -48,7 +49,7 @@ SOH_LOGGING_CONFIG = {
# MQTT logging configuration parameters
MQTT_LOGGING_CONFIG = {
'hostname': 'ohmpy.umons.ac.be',
'hostname': 'raspberrypi.local',
'port': 1883,
'qos': 0,
'retain': False,
......
In this folder will be logged all measurements done with the OhmPi as .csv.
env.sh 100644 → 100755
#!/bin/bash
#!/bin/bash
sudo apt-get install -y libatlas-base-dev
python3 -m venv ohmpy
source ohmpy/bin/activate || exit 1 # NOTE: Added || exit to avoid installing requirements in system python
......
This diff is collapsed.
File mode changed from 100644 to 100755
......@@ -120,7 +120,7 @@ class OhmPi(object):
- injection_duration (in seconds)
- nbr_meas (total number of times the sequence will be run)
- sequence_delay (delay in second between each sequence run)
- stack (number of stack for each quadrupole measurement)
- nb_stack (number of stack for each quadrupole measurement)
- export_path (path where to export the data, timestamp will be added to filename)
Parameters
......@@ -375,7 +375,7 @@ class OhmPi(object):
# TODO here we can add the current_injected or voltage_injected in mA or mV
# check arguments
if nb_stack is None:
nb_stack = self.pardict['stack']
nb_stack = self.pardict['nb_stack']
if injection_duration is None:
injection_duration = self.pardict['injection_duration']
......@@ -410,8 +410,7 @@ class OhmPi(object):
gain_voltage = self.gain_auto(AnalogIn(self.ads_voltage, ads.P0, ads.P1))
pin0.value = False
pin1.value = False
print(gain_current)
print(gain_voltage)
print('gain current: {:.3f}, gain voltage: {:.3f}'.format(gain_current, gain_voltage))
self.ads_current = ads.ADS1115(self.i2c, gain=gain_current, data_rate=860, address=0x48)
self.ads_voltage = ads.ADS1115(self.i2c, gain=gain_voltage, data_rate=860, address=0x49)
......@@ -465,20 +464,20 @@ class OhmPi(object):
# create a dictionary and compute averaged values from all stacks
d = {
"time": [datetime.now().isoformat()],
"time": datetime.now().isoformat(),
"A": quad[0],
"B": quad[1],
"M": quad[2],
"N": quad[3],
"inj time [ms]": (end_delay - start_delay) * 1000,
"Vmn [mV]": [(sum_vmn / (3 + 2 * nb_stack - 1))],
"I [mA]": [(injection_current / (3 + 2 * nb_stack - 1))],
"R [ohm]": [(sum_vmn / (3 + 2 * nb_stack - 1) / (injection_current / (3 + 2 * nb_stack - 1)))],
"Ps [mV]": [(sum_ps / (3 + 2 * nb_stack - 1))],
"nbStack": [nb_stack],
"CPU temp [degC]": [CPUTemperature().temperature],
"Time [s]": [(-start_time + time.time())],
"Nb samples [-]": [self.nb_samples]
"Vmn [mV]": (sum_vmn / (3 + 2 * nb_stack - 1)),
"I [mA]": (injection_current / (3 + 2 * nb_stack - 1)),
"R [ohm]": (sum_vmn / (3 + 2 * nb_stack - 1) / (injection_current / (3 + 2 * nb_stack - 1))),
"Ps [mV]": (sum_ps / (3 + 2 * nb_stack - 1)),
"nbStack": nb_stack,
"CPU temp [degC]": CPUTemperature().temperature,
"Time [s]": (-start_time + time.time()),
"Nb samples [-]": self.nb_samples
}
# round number to two decimal for nicer string output
......@@ -500,18 +499,45 @@ class OhmPi(object):
""" Check contact resistance.
"""
# create custom sequence where MN == AB
nelec = self.sequence.max() # number of elec used in the sequence
# we only check the electrodes which are in the sequence (not all might be connected)
elec = np.sort(np.unique(self.sequence.flatten())) # assumed order
quads = np.vstack([
np.arange(nelec - 1) + 1,
np.arange(nelec - 1) + 2,
np.arange(nelec - 1) + 1,
np.arange(nelec - 1) + 2
elec[:-1],
elec[1:],
elec[:-1],
elec[1:],
]).T
# create filename to store RS
export_path_rs = self.pardict['export_path'].replace('.csv', '') \
+ '_' + datetime.now().strftime('%Y%m%dT%H%M%S') + '_rs.csv'
# perform RS check
self.run = True
self.status = 'running'
# make sure all mux are off to start with
self.reset_mux()
# measure all quad of the RS sequence
for i in range(0, quads.shape[0]):
quad = quads[i, :] # quadrupole
self.reset_mux()
self.switch_mux_on(quad)
# NOTE (GB): I'd use the self.run_measurement() for all this middle part so we an make use of autogain and so ...
# call the switch_mux function to switch to the right electrodes
#self.switch_mux_on(quad)
# run a measurement
#current_measurement = self.run_measurement(quad, 1, 0.25)
# switch mux off
#self.switch_mux_off(quad)
# save data and print in a text file
#self.append_and_save(export_path_rs, current_measurement)
# current injection
pin0 = self.mcp.get_pin(0)
pin0.direction = Direction.OUTPUT
pin1 = self.mcp.get_pin(1)
......@@ -519,40 +545,51 @@ class OhmPi(object):
pin0.value = False
pin1.value = False
print(quad)
# call the switch_mux function to switch to the right electrodes
self.switch_mux_on(quad)
self.ads_current = ads.ADS1115(self.i2c, gain=2 / 3, data_rate=860, address=0x48)
# ADS1115 for voltage measurement (MN)
self.ads_voltage = ads.ADS1115(self.i2c, gain=2 / 3, data_rate=860, address=0x49)
pin1.value = True
pin1.value = True # inject from pin1 to pin0
pin0.value = False
time.sleep(0.2)
# measure current and voltage
current = AnalogIn(self.ads_current, ads.P0).voltage / (50 * self.r_shunt)
voltage = -AnalogIn(self.ads_voltage, ads.P0, ads.P1).voltage * 2.5
resistance = voltage / current
# print(B)
# print(A)
print(abs(round(resistance / 1000, 1)), "kOhm")
# compute resistance measured (= contact resistance)
resist = abs(resistance / 1000)
msg = 'Contact resistance {:s}: {:.3f} kOhm'.format(
str(quad), resist)
print(msg)
self.exec_logger.debug(msg)
# if contact resistance = 0 -> we have a short circuit!!
if resist < 1e-5:
msg = '!!!SHORT CIRCUIT!!! {:s}: {:.3f} kOhm'.format(
str(quad), resist)
self.exec_logger.warning(msg)
print(msg)
# save data and print in a text file
self.append_and_save(export_path_rs, {
'A': quad[0],
'B': quad[1],
'RS [kOhm]': resist,
})
# close mux path and put pin back to GND
self.switch_mux_off(quad)
pin0.value = False
pin1.value = False
self.reset_mux()
self.status = 'idle'
self.run = False
# # create backup TODO not good
# export_path = self.pardict['export_path']
# sequence = self.sequence.copy()
#
# # assign new value
# self.pardict['export_path'] = export_path.replace('.csv', '_rs.csv')
# self.sequence = quads
# print(self.sequence)
#
# # run the RS check
# self.log_exec('RS check (check contact resistance)', level='debug')
# self.measure()
#
# # restore
# self.pardict['export_path'] = export_path
# self.sequence = sequence
#
# # TODO if interrupted, we would need to restore the values
# # TODO or we offer the possiblity in 'run_measurement' to have rs_check each time?
......@@ -616,7 +653,7 @@ class OhmPi(object):
# run a measurement
if self.on_pi:
current_measurement = self.run_measurement(quad, self.pardict["stack"],
current_measurement = self.run_measurement(quad, self.pardict["nb_stack"],
self.pardict["injection_duration"])
else: # for testing, generate random data
current_measurement = {
......@@ -680,7 +717,7 @@ if on_pi:
# and emit a warning otherwise
if not arm64_imports:
print(colored(f'Warning: Required packages are missing.\n'
f'Please run . env.sh at command prompt to update your virtual environment\n', 'yellow'))
f'Please run ./env.sh at command prompt to update your virtual environment\n', 'yellow'))
else:
print(colored(f'Not running on the Raspberry Pi platform.\nFor simulation purposes only...', 'yellow'))
......
......@@ -3,6 +3,6 @@
"injection_duration":0.2,
"nbr_meas": 100,
"sequence_delay": 1,
"stack": 1,
"nb_stack": 1,
"export_path": "data/measurement.csv"
}
run.sh 100644 → 100755
source ./ohmpy/bin/activate
python3 webserver.py
python webserver.py
echo "# start OhmPi web interface" >> $HOME/.bashrc
echo "(cd $HOME/OhmPi; ./run.sh)" >> $HOME/.bashrc
from http.server import SimpleHTTPRequestHandler, HTTPServer
# import time
import os
import json
from ohmpi import OhmPi
# import threading
import pandas as pd
import shutil
hostName = 'localhost'
serverPort = 8080
# https://gist.github.com/MichaelCurrie/19394abc19abd0de4473b595c0e37a3a
with open('ohmpi_param.json') as json_file:
pardict = json.load(json_file)
ohmpi = OhmPi(pardict)
class MyServer(SimpleHTTPRequestHandler):
# because we use SimpleHTTPRequestHandler, we do not need to implement
# the do_GET() method (if we use the BaseHTTPRequestHandler, we would need to)
# def do_GET(self):
# # normal get for wepages (not so secure!)
# print(self.command)
# print(self.headers)
# print(self.request)
# self.send_response(200)
# self.send_header("Content-type", "text/html")
# self.end_headers()
# with open(os.path.join('.', self.path[1:]), 'r') as f:
# self.wfile.write(bytes(f.read(), "utf-8"))
def do_POST(self):
# global ohmpiThread, status, run
dic = json.loads(self.rfile.read(int(self.headers['Content-Length'])))
rdic = {}
if dic['command'] == 'start':
ohmpi.measure()
elif dic['command'] == 'stop':
ohmpi.stop()
elif dic['command'] == 'getData':
# get all .csv file in data folder
fnames = os.listdir('data/')
ddic = {}
for fname in fnames:
if fname.replace('.csv', '') not in dic['surveyNames'] and fname != 'readme.txt':
df = pd.read_csv('data/' + fname)
ddic[fname.replace('.csv', '')] = {
'a': df['A'].tolist(),
'b': df['B'].tolist(),
'm': df['M'].tolist(),
'n': df['N'].tolist(),
'rho': df['R [ohm]'].tolist(),
}
rdic['data'] = ddic
elif dic['command'] == 'removeData':
shutil.rmtree('data')
os.mkdir('data')
elif dic['command'] == 'setConfig':
ohmpi.stop()
cdic = dic['config']
ohmpi.pardict['nb_electrodes'] = int(cdic['nbElectrodes'])
ohmpi.pardict['injection_duration'] = float(cdic['injectionDuration'])
ohmpi.pardict['nbr_meas'] = int(cdic['nbMeasurements'])
ohmpi.pardict['stack'] = int(cdic['nbStack'])
ohmpi.pardict['sequence_delay'] = int(cdic['sequenceDelay'])
print('setConfig', ohmpi.pardict)
elif dic['command'] == 'invert':
pass
elif dic['command'] == 'getResults':
pass
elif dic['command'] == 'download':
shutil.make_archive('data', 'zip', 'data')
else:
# command not found
rdic['response'] = 'command not found'
rdic['status'] = ohmpi.status
self.send_response(200)
self.send_header('Content-Type', 'text/json')
self.end_headers()
self.wfile.write(bytes(json.dumps(rdic), 'utf8'))
if __name__ == "__main__":
webServer = HTTPServer((hostName, serverPort), MyServer)
print("Server started http://%s:%s" % (hostName, serverPort))
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()
print("Server stopped.")
from http.server import SimpleHTTPRequestHandler, HTTPServer
import time
import os
import json
from ohmpi import OhmPi
import threading
import pandas as pd
import shutil
hostName = "raspberrypi.local" # works for AP-STA
#hostName = "192.168.50.1" # fixed IP in AP-STA mode
#hostName = "0.0.0.0" # for AP mode (not AP-STA)
serverPort = 8080
# https://gist.github.com/MichaelCurrie/19394abc19abd0de4473b595c0e37a3a
with open('ohmpi_param.json') as json_file:
pardict = json.load(json_file)
ohmpi = OhmPi(pardict, sequence='breadboard.txt')
#ohmpi = OhmPi(pardict, sequence='dd16s0no8.txt')
class MyServer(SimpleHTTPRequestHandler):
# because we use SimpleHTTPRequestHandler, we do not need to implement
# the do_GET() method (if we use the BaseHTTPRequestHandler, we would need to)
# def do_GET(self):
# # normal get for wepages (not so secure!)
# print(self.command)
# print(self.headers)
# print(self.request)
# self.send_response(200)
# self.send_header("Content-type", "text/html")
# self.end_headers()
# with open(os.path.join('.', self.path[1:]), 'r') as f:
# self.wfile.write(bytes(f.read(), "utf-8"))
def do_POST(self):
global ohmpiThread, status, run
dic = json.loads(self.rfile.read(int(self.headers['Content-Length'])))
rdic = {}
if dic['command'] == 'start':
ohmpi.measure()
elif dic['command'] == 'stop':
ohmpi.stop()
elif dic['command'] == 'getData':
# get all .csv file in data folder
fnames = [fname for fname in os.listdir('data/') if fname[-4:] == '.csv']
ddic = {}
for fname in fnames:
if (fname.replace('.csv', '') not in dic['surveyNames']
and fname != 'readme.txt'
and '_rs' not in fname):
df = pd.read_csv('data/' + fname)
ddic[fname.replace('.csv', '')] = {
'a': df['A'].tolist(),
'b': df['B'].tolist(),
'm': df['M'].tolist(),
'n': df['N'].tolist(),
'rho': df['R [ohm]'].tolist(),
}
rdic['data'] = ddic
elif dic['command'] == 'removeData':
shutil.rmtree('data')
os.mkdir('data')
elif dic['command'] == 'setConfig':
ohmpi.stop()
cdic = dic['config']
ohmpi.pardict['nb_electrodes'] = int(cdic['nbElectrodes'])
ohmpi.pardict['injection_duration'] = float(cdic['injectionDuration'])
ohmpi.pardict['nbr_meas'] = int(cdic['nbMeasurements'])
ohmpi.pardict['nb_stack'] = int(cdic['nbStack'])
ohmpi.pardict['sequence_delay'] = int(cdic['sequenceDelay'])
if cdic['sequence'] != '':
with open('sequence.txt', 'w') as f:
f.write(cdic['sequence'])
ohmpi.read_quad('sequence.txt')
print('new sequence set.')
print('setConfig', ohmpi.pardict)
elif dic['command'] == 'invert':
pass
elif dic['command'] == 'getResults':
pass
elif dic['command'] == 'rsCheck':
ohmpi.rs_check()
fnames = sorted([fname for fname in os.listdir('data/') if fname[-7:] == '_rs.csv'])
df = pd.read_csv('data/' + fnames[-1])
ddic = {
'AB': (df['A'].astype('str') + '-' + df['B'].astype(str)).tolist(),
'res': df['RS [kOhm]'].tolist()
}
rdic['data'] = ddic
elif dic['command'] == 'download':
shutil.make_archive('data', 'zip', 'data')
elif dic['command'] == 'shutdown':
print('shutting down...')
os.system('shutdown now -h')
elif dic['command'] == 'restart':
print('shutting down...')
os.system('reboot')
else:
# command not found
rdic['response'] = 'command not found'
rdic['status'] = ohmpi.status
self.send_response(200)
self.send_header('Content-Type', 'text/json')
self.end_headers()
self.wfile.write(bytes(json.dumps(rdic), 'utf8'))
if __name__ == "__main__":
webServer = HTTPServer((hostName, serverPort), MyServer)
print("Server started http://%s:%s" % (hostName, serverPort))
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()
print("Server stopped.")
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment