Skip to content

Commit cb1424a

Browse files
author
Eric
committed
IMU | Ep.1: Preparing an experiment to test linear positions (ft. MPU6050, GY-BNO055)
0 parents  commit cb1424a

File tree

7 files changed

+381
-0
lines changed

7 files changed

+381
-0
lines changed

ESP32_GY-BNO055/ESP32_GY-BNO055.ino

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include <Wire.h>
2+
#include <Adafruit_Sensor.h>
3+
#include <Adafruit_BNO055.h>
4+
#include <TFT_eSPI.h>
5+
#define I2C_SDA 21
6+
#define I2C_SCL 22
7+
8+
TwoWire I2CBNO = TwoWire(0);
9+
Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x29, &I2CBNO);
10+
TFT_eSPI tft = TFT_eSPI();
11+
12+
void setup(){
13+
Serial.begin(115200);
14+
displayInit();
15+
16+
I2CBNO.begin(I2C_SDA, I2C_SCL);
17+
18+
if (!bno.begin()){
19+
printString("No BNO055 detected", TFT_BLACK, TFT_YELLOW);
20+
while (1){
21+
delay(10);
22+
}
23+
}
24+
25+
delay(100);
26+
27+
uint8_t system, gyro, accel, mag = 0;
28+
while(system != 3) {
29+
bno.getCalibration(&system, &gyro, &accel, &mag);
30+
printString("Calibration!!!", TFT_WHITE, TFT_RED);
31+
delay(100);
32+
}
33+
34+
printString("READING...", TFT_BLACK, TFT_DARKGREEN);
35+
delay(100);
36+
}
37+
38+
void loop(){
39+
40+
delay(8); // To make sampling rate around 100hz
41+
42+
imu::Vector<3> acc = bno.getVector(Adafruit_BNO055::VECTOR_ACCELEROMETER);
43+
imu::Vector<3> gyr = bno.getVector(Adafruit_BNO055::VECTOR_GYROSCOPE);
44+
45+
Serial.print(gyr.x(),4);
46+
Serial.print(", ");
47+
Serial.print(gyr.y(),4);
48+
Serial.print(", ");
49+
Serial.print(gyr.z(),4);
50+
Serial.print(", ");
51+
Serial.print(acc.x(), 4);
52+
Serial.print(", ");
53+
Serial.print(acc.y(),4);
54+
Serial.print(", ");
55+
Serial.println(acc.z(),4);
56+
}
57+
58+
void displayInit(){
59+
tft.begin();
60+
tft.setRotation(1);
61+
tft.setTextColor(TFT_WHITE,TFT_BLACK);
62+
tft.fillScreen(TFT_BLACK);
63+
tft.setSwapBytes(true);
64+
tft.setTextFont(4);
65+
tft.setTextDatum(MC_DATUM);
66+
}
67+
68+
void printString(String text, uint16_t textColor, uint16_t bgColor){
69+
tft.fillScreen(bgColor);
70+
tft.setTextColor(textColor, bgColor);
71+
tft.drawCentreString(text, tft.width()/2, tft.height()/2, 4);
72+
}

ESP32_MPU6050/ESP32_MPU6050.ino

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include <Adafruit_MPU6050.h>
2+
#include <Adafruit_Sensor.h>
3+
#include <Wire.h>
4+
#include <TFT_eSPI.h>
5+
#define I2C_SDA 21
6+
#define I2C_SCL 22
7+
8+
TwoWire I2CMPU = TwoWire(0);
9+
Adafruit_MPU6050 mpu;
10+
TFT_eSPI tft = TFT_eSPI();
11+
12+
void setup(){
13+
Serial.begin(115200);
14+
displayInit();
15+
16+
I2CMPU.begin(I2C_SDA, I2C_SCL);
17+
18+
if (!mpu.begin(104, &I2CMPU, 0x68)) {
19+
printString("No MPU6050 detected", TFT_BLACK, TFT_YELLOW);
20+
while (1) {
21+
delay(10);
22+
}
23+
}
24+
25+
delay(100);
26+
27+
mpu.setAccelerometerRange(MPU6050_RANGE_4_G);
28+
mpu.setGyroRange(MPU6050_RANGE_2000_DEG);
29+
30+
printString("READING...", TFT_BLACK, TFT_DARKGREEN);
31+
delay(100);
32+
}
33+
34+
void loop(){
35+
36+
delay(7); // To make sampling rate around 100hz
37+
38+
sensors_event_t a, g, temp;
39+
mpu.getEvent(&a, &g, &temp);
40+
41+
Serial.print(g.gyro.x);
42+
Serial.print(", ");
43+
Serial.print(g.gyro.y);
44+
Serial.print(", ");
45+
Serial.print(g.gyro.z);
46+
Serial.print(", ");
47+
48+
Serial.print(a.acceleration.x);
49+
Serial.print(", ");
50+
Serial.print(a.acceleration.y);
51+
Serial.print(", ");
52+
Serial.println(a.acceleration.z);
53+
}
54+
55+
void displayInit(){
56+
tft.begin();
57+
tft.setRotation(1);
58+
tft.setTextColor(TFT_WHITE,TFT_BLACK);
59+
tft.fillScreen(TFT_BLACK);
60+
tft.setSwapBytes(true);
61+
tft.setTextFont(4);
62+
tft.setTextDatum(MC_DATUM);
63+
}
64+
65+
void printString(String text, uint16_t textColor, uint16_t bgColor){
66+
tft.fillScreen(bgColor);
67+
tft.setTextColor(textColor, bgColor);
68+
tft.drawCentreString(text, tft.width()/2, tft.height()/2, 4);
69+
}

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Python App for Data Sampling via Serial to CSV file
2+
3+
![](app_demo.gif)
4+
5+
You can easily save accelerometer and gyro readings from BNO055 or MPU6050 to your end device via Serial Comm. It was created to analyze the data obtained from the sensor and the GUI was configured using the PySimpleGUI.
6+
7+
## Requirements
8+
- python3
9+
- pip3 install -r requirements.txt
10+
11+
## More information is available here
12+
* [IMU | Ep.1: Preparing an experiment to test linear positions (ft. MPU6050, GY-BNO055)][[Video]](https://youtu.be/3-IBOJ5FQvI)
13+
14+
### Created & Maintained By
15+
[Eric Nam](https://github.com/0015)
16+
([Youtube](https://www.youtube.com/channel/UCRr2LnXXXuHn4z0rBvpfG7w))
17+
([Facebook](https://www.facebook.com/groups/138965931539175))
18+
19+
## License
20+
21+
Copyright (c) 2021 Eric N
22+
23+
Permission is hereby granted, free of charge, to any person obtaining
24+
a copy of this software and associated documentation files (the
25+
"Software"), to deal in the Software without restriction, including
26+
without limitation the rights to use, copy, modify, merge, publish,
27+
distribute, sublicense, and/or sell copies of the Software, and to
28+
permit persons to whom the Software is furnished to do so, subject to
29+
the following conditions:
30+
31+
The above copyright notice and this permission notice shall be
32+
included in all copies or substantial portions of the Software.
33+
34+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
35+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
36+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
37+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
38+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
39+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
40+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41+
42+
43+

app_demo.gif

14.1 MB
Loading

main.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import csv
2+
import queue
3+
import re
4+
import threading
5+
from time import perf_counter
6+
7+
import PySimpleGUI as sg
8+
9+
import serial_comm as my_serial
10+
11+
12+
class Application:
13+
14+
def __init__(self, *args, **kwargs):
15+
super(Application, self).__init__(*args, **kwargs)
16+
baud_rate = 115200
17+
gui_queue = queue.Queue()
18+
serial_connector = my_serial.SerialObj(baud_rate)
19+
20+
headerFont = ('Helvetica', 16)
21+
middleFont = ('Helvetica', 14)
22+
contextFont = ('Helvetica', 12)
23+
smallFont = ('Helvetica', 10)
24+
sg.theme('DarkBlue')
25+
26+
layout = [[sg.Text('GET ACCELEROMETER GYROSCOPE\nSAMPLING DATA VIA SERIAL', font=headerFont)],
27+
[sg.Text('Select your serial port', font=contextFont),
28+
sg.Button('Serial Port Reload', size=(20, 1), font=smallFont)],
29+
[sg.Listbox(values=[x[0] for x in my_serial.SerialObj.get_ports()],
30+
size=(40, 6),
31+
key='_SERIAL_PORT_LIST_',
32+
font=contextFont,
33+
enable_events=True)],
34+
[sg.Text('', key='_SERIAL_PORT_CONFIRM_', size=(40, 1), font=middleFont, ), ],
35+
[sg.Text('Buad Rate: {} bps'.format(baud_rate), size=(40, 1), font=middleFont, ), ],
36+
[sg.Text('How many samples?', font=contextFont, ), sg.VerticalSeparator(),
37+
sg.Input(do_not_clear=True, enable_events=True, key='_SAMPLE_IN_', font=contextFont, )],
38+
39+
[sg.HorizontalSeparator()],
40+
[sg.Text('Serial Comm Status', font=contextFont, pad=((6, 0), (20, 0))), ],
41+
[sg.Text('', key='_OUTPUT_', size=(40, 2), font=middleFont, ), ],
42+
[sg.Button('Start', key='_ACT_BUTTON_', font=middleFont, size=(40, 1), pad=((0, 0), (0, 0)))],
43+
[sg.Button('Exit', font=middleFont, size=(40, 1), pad=((0, 0), (20, 0)))],
44+
[sg.Text('ThatProject - Version: 0.1', justification='right', size=(50, 1), font=smallFont, ), ]]
45+
46+
self.window = sg.Window('Simple Serial Application', layout, size=(320, 440), keep_on_top=True)
47+
48+
while True:
49+
event, values = self.window.Read(timeout=100)
50+
51+
if event is None or event == 'Exit':
52+
break
53+
54+
if event == 'Serial Port Reload':
55+
self.get_ports()
56+
57+
if event == '_SERIAL_PORT_LIST_':
58+
self.window['_SERIAL_PORT_CONFIRM_'].update(value=self.window['_SERIAL_PORT_LIST_'].get()[0])
59+
60+
if event == '_SAMPLE_IN_' and values['_SAMPLE_IN_'] and values['_SAMPLE_IN_'][-1] not in ('0123456789'):
61+
self.window['_SAMPLE_IN_'].update(values['_SAMPLE_IN_'][:-1])
62+
63+
if event == '_ACT_BUTTON_':
64+
print(self.window[event].get_text())
65+
if self.window[event].get_text() == 'Start':
66+
67+
if len(self.window['_SERIAL_PORT_LIST_'].get()) == 0:
68+
self.popup_dialog('Serial Port is not selected yet!', 'Serial Port', contextFont)
69+
70+
elif len(self.window['_SAMPLE_IN_'].get()) == 0:
71+
self.popup_dialog('Set Sampling Count', 'Sampling Number Error', contextFont)
72+
73+
else:
74+
self.stop_thread_trigger = False
75+
self.thread_serial = threading.Thread(target=self.start_serial_comm,
76+
args=(serial_connector,
77+
self.window[
78+
'_SERIAL_PORT_LIST_'].get()[
79+
0],
80+
int(self.window[
81+
'_SAMPLE_IN_'].get()),
82+
gui_queue, lambda: self.stop_thread_trigger),
83+
daemon=True)
84+
self.thread_serial.start()
85+
self.window['_ACT_BUTTON_'].update('Stop')
86+
87+
else:
88+
self.stop_thread_trigger = True
89+
self.thread_serial.join()
90+
self.window['_ACT_BUTTON_'].update('Start')
91+
92+
try:
93+
message = gui_queue.get_nowait()
94+
except queue.Empty:
95+
message = None
96+
if message is not None:
97+
self.window['_OUTPUT_'].Update(message)
98+
if 'Done' in message:
99+
self.window['_ACT_BUTTON_'].update('Start')
100+
self.popup_dialog(message, 'Success', contextFont)
101+
102+
self.window.Close()
103+
104+
def popup_dialog(self, contents, title, font):
105+
sg.Popup(contents, title=title, keep_on_top=True, font=font)
106+
107+
def get_ports(self):
108+
self.window['_SERIAL_PORT_LIST_'].Update(values=[x[0] for x in my_serial.SerialObj.get_ports()])
109+
110+
def start_serial_comm(self, serial_connector, serialport, sample_num, gui_queue, stop_thread_trigger):
111+
112+
start_time = 0
113+
114+
serial_connector.connect(serialport)
115+
if serial_connector.is_connect():
116+
117+
gui_queue.put('Serial Connected!!')
118+
119+
n = 0
120+
while n < sample_num:
121+
122+
try:
123+
if stop_thread_trigger():
124+
break
125+
126+
data = serial_connector.get_data()
127+
if data is not None:
128+
129+
if n == 0:
130+
gui_queue.put(' - Data Transmitting ::: Wait! ')
131+
start_time = perf_counter()
132+
133+
decode_string = data.decode('utf-8')
134+
print(decode_string)
135+
if len(decode_string.split(',')) == 6:
136+
n += 1
137+
percent = n / sample_num * 100
138+
self.csv_writer('LoggedData_CalInertialAndMag.csv', n, decode_string)
139+
140+
if percent % 10 == 0:
141+
gui_queue.put('Saving to CSV File: {}% complete'.format(int(percent)))
142+
143+
except OSError as error:
144+
print(error)
145+
146+
except UnicodeDecodeError as error:
147+
print(error)
148+
149+
serial_connector.disconnect()
150+
time_taken = (perf_counter() - start_time)
151+
sampling_rate = sample_num / time_taken
152+
gui_queue.put('Sampling Rate: {} hz ::: Done!'.format(int(sampling_rate)))
153+
return
154+
155+
def csv_writer(self, filename, index, data):
156+
with open(filename, 'a') as f:
157+
writer = csv.writer(f, delimiter=",", quoting=csv.QUOTE_NONE, escapechar=' ')
158+
writer.writerow([index, re.sub(r"\s+", "", data), 0, 0,
159+
0]) # Dummy data for magnetometers, it doesn't use magnetometer in matlab.
160+
161+
162+
if __name__ == '__main__':
163+
Application()

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pysimplegui
2+
pyserial

serial_comm.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import serial
2+
import serial.tools.list_ports
3+
4+
5+
class SerialObj:
6+
7+
def __init__(self, serial_speed):
8+
self.serial = None
9+
self.baud_rate = serial_speed
10+
11+
@staticmethod
12+
def get_ports():
13+
return list(serial.tools.list_ports.comports())
14+
15+
def connect(self, port):
16+
self.serial = serial.Serial(port, self.baud_rate)
17+
self.serial.flushInput()
18+
19+
def is_connect(self):
20+
return self.serial.isOpen()
21+
22+
def get_data(self):
23+
if not self.serial.isOpen():
24+
return None
25+
else:
26+
return self.serial.readline()
27+
28+
def disconnect(self):
29+
if self.serial is None:
30+
return
31+
32+
self.serial.close()

0 commit comments

Comments
 (0)