Initial Commit

This commit is contained in:
janis
2022-05-14 18:04:52 +02:00
commit 3fade3a2c9
20 changed files with 587 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

8
.idea/micro_bit_interface.iml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

4
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/micro_bit_interface.iml" filepath="$PROJECT_DIR$/.idea/micro_bit_interface.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

5
bin/com/communication.py Normal file
View File

@@ -0,0 +1,5 @@
class Communication:
def __init__(self):
self.__x = 0
self.__data_recieve = 0
self.__output = ""

24
bin/com/comport_search.py Normal file
View File

@@ -0,0 +1,24 @@
import serial.tools.list_ports
class ComportService:
def __init__(self):
self.__comport = []
self.__import = []
self.__working = []
self.__pos = 0
self.__com_name = ""
def get_comport(self, special_port=""):
self.__comport = [comport.device for comport in serial.tools.list_ports.comports()]
self.__pos = 0
if special_port != "":
self.__working = special_port
else:
while self.__working == []:
self.__com_name = serial.tools.list_ports.comports()[self.__pos]
if "USB-Serial Controller" or "Prolific USB-Serial Controller" in self.__com_name:
self.__working = self.__comport.pop(self.__pos)
else:
self.__pos += 1
return self.__working

122
bin/com/csv_parsers.py Normal file
View File

@@ -0,0 +1,122 @@
"""@package docstring
This is a simplification of the csv module"""
import csv
class CsvRead:
"""This is a class that reads csv files and depending on the module selected does do different things with it"""
def __init__(self):
self.__imp = ""
self.__raw = ""
self.__raw_list = ""
def importing(self, path):
"""Returns a list of the imported csv-file, requires path, either direct system path or relative path"""
self.__imp = open(path)
self.__raw = csv.reader(self.__imp, delimiter=',')
self.__raw_list = list(self.__raw)
self.__imp.close()
return self.__raw_list
class CsvWrite:
"""This is a class that modifies csv files"""
def __init__(self):
self.__impl = []
self.__strpop = []
self.__removed = []
self.__removing = 0
self.__change = 0
self.__appending = 0
self.__imp = []
self.__raw = []
def rem_str(self, path, row):
"""Opens the csv-file in write mode which is specified as an argument either as direct or relative path"""
self.__imp = open(path)
self.__raw = csv.reader(self.__imp, delimiter=',')
self.__impl = list(self.__raw)
self.__removed = self.__impl.pop(row + 1)
with open(path, "w") as removedata:
self.__removing = csv.writer(removedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
self.__removing.writerow(self.__impl.pop(0))
while len(self.__impl) > 0:
with open(path, "a") as removedata:
self.__removing = csv.writer(removedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
self.__removing.writerow(self.__impl.pop(0))
self.__imp.close()
removedata.close()
def chg_str(self, path, row, pos, new_value):
"""Opens the csv-file in write mode to change a value, e.g. if a recipes is changed."""
self.__imp = open(path)
self.__raw = csv.reader(self.__imp, delimiter=',')
self.__impl = list(self.__raw)
self.__strpop = self.__impl.pop(row)
self.__strpop.pop(pos)
self.__strpop.insert(pos, new_value)
self.__impl.insert(row, self.__strpop)
with open(path, "w") as changedata:
self.__change = csv.writer(changedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
self.__change.writerow(self.__impl.pop(0))
while len(self.__impl) > 0:
with open(path, "a") as changedata:
self.__removing = csv.writer(changedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
self.__removing.writerow(self.__impl.pop(0))
self.__imp.close()
changedata.close()
def chg_str_rem(self, path, row, pos):
"""Opens the csv-file in write mode to change a value, e.g. if a recipes is changed."""
self.__imp = open(path)
self.__raw = csv.reader(self.__imp, delimiter=',')
self.__impl = list(self.__raw)
self.__strpop = self.__impl.pop(row)
self.__strpop.pop(pos)
self.__strpop.pop(pos)
self.__impl.insert(row, self.__strpop)
with open(path, "w") as changedata:
self.__change = csv.writer(changedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
self.__change.writerow(self.__impl.pop(0))
while len(self.__impl) > 0:
with open(path, "a") as changedata:
self.__removing = csv.writer(changedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
self.__removing.writerow(self.__impl.pop(0))
self.__imp.close()
changedata.close()
def chg_str_add(self, path, row, new_value1, new_value2):
"""Opens the csv-file in write mode to change a value, e.g. if a recipes is changed."""
self.__imp = open(path)
self.__raw = csv.reader(self.__imp, delimiter=',')
self.__impl = list(self.__raw)
self.__strpop = self.__impl.pop(row)
self.__strpop.append(new_value1)
self.__strpop.append(new_value2)
self.__impl.insert(row, self.__strpop)
with open(path, "w") as changedata:
self.__change = csv.writer(changedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
self.__change.writerow(self.__impl.pop(0))
while len(self.__impl) > 0:
with open(path, "a") as changedata:
self.__removing = csv.writer(changedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
self.__removing.writerow(self.__impl.pop(0))
self.__imp.close()
changedata.close()
def app_str(self, path, value):
"""Opens the csv-file in append mode and writes given input. CsvWrite.app_str(path, value).
Path can be specified both as direct or relative. value is a list. Will return an error if type of value is
not a list."""
with open(path, "a") as appenddata:
self.__appending = csv.writer(appenddata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
self.__appending.writerow(value)
appenddata.close()
def write_str(self, path, value):
with open(path, "w") as writedata:
self.__change = csv.writer(writedata, delimiter=',', quoting=csv.QUOTE_MINIMAL)
self.__change.writerow(value)
writedata.close()

73
bin/com/lib.py Normal file
View File

@@ -0,0 +1,73 @@
import serial
import struct
import bin.com.comport_search
"""@package docstring
This package can communicate with a microcontroller"""
coms = bin.com.comport_search.ComportService()
class Com:
def __init__(self):
self.xr = ""
self.output = ""
self.str_input = ""
self.str_get_input = ""
self.xs = ""
self.__comport = '/dev/ttyUSB0'
def connect(self, baudrate, special_port):
try:
self.__comport = coms.get_comport(special_port)
except:
pass
self.ser = serial.Serial(self.__comport, baudrate=baudrate, timeout=5)
def quitcom(self):
try:
self.ser.close()
except:
pass
def receive(self, amount_bytes):
self.xr = self.ser.read(amount_bytes)
return self.xr
def decode_ascii(self, value):
try:
self.output = value.decode()
except:
self.output = "Error"
return self.output
def check_value(self, value_check, checked_value):
if value_check == checked_value:
return 1
else:
return 0
def decode_int(self, value):
self.i = int(value, base = 16)
return self.i
def decode_float(self, value):
self.fs = str(value, 'ascii') + '00'
self.f = struct.unpack('>f', bytes.fromhex(self.fs))
return str(self.f[0])
def decode_float_2(self, value):
self.fs = str(value, 'ascii') + '0000'
self.f = struct.unpack('>f', bytes.fromhex(self.fs))
return str(self.f[0])
def get_input(self):
self.str_get_input = input("please enter a character to send: ")
return self.str_get_input
def send(self, str_input):
self.xs = str_input.encode()
self.ser.write(self.xs)
def send_float(self, float_input):
ba = bytearray(struct.pack('>f', float_input))
self.ser.write(ba[0:3])

View File

@@ -0,0 +1 @@
test1,test2,stop,start,transmit,test,arch
1 test1 test2 stop start transmit test arch

27
bin/gui/command_screen.kv Normal file
View File

@@ -0,0 +1,27 @@
Command:
name: "CommandScreen"
md_bg_color: 0, 0, 0, 1
FloatLayout:
Label:
id: cmd_out
size_hint: 0.96, 0.6
pos_hint: {"x": 0.03, "y": 0.35}
text_size: self.size
color: 1, 1, 1, 1
text: ""
valign: "bottom"
TextInput:
color: 1, 1, 1, 1
foreground_color: 1, 1, 1, 1
background_color: 0, 0, 0, 1
size_hint: 0.96, 0.1
pos_hint: {"x": 0.02, "y": 0.2}
id: tin
hint_text: "Enter command..."
multiline: False
on_text: root.autocomplete()
on_text_validate: root.runcommand()
Button:
text: "back"
size_hint: 0.1, 0.1
pos_hint: {"x": 0.02, "y": 0.02}

23
bin/gui/load_screen.kv Normal file
View File

@@ -0,0 +1,23 @@
Load:
name: "LoadScreen"
on_enter: root.start_pb()
radius: [25, 25, 25, 25]
md_bg_color: app.theme_cls.accent_light
GridLayout:
cols: 1
Label:
text: "micro:bit interface"
font_size: 60
color: app.theme_cls.primary_dark
FloatLayout:
MDProgressBar:
id: progress
size_hint: .6, .6
pos_hint: {"x": 0.2, "y": 0.3}
color: app.theme_cls.primary_dark
type: "determinate"
running_duration: 0.75
catching_duration: 0.5
Label:
color: "black"
text: "starting app ..."

17
bin/gui/main_screen.kv Normal file
View File

@@ -0,0 +1,17 @@
Main:
name: "HomeScreen"
md_bg_color: app.theme_cls.accent_light
GridLayout:
cols: 1
Label:
text: "micro:bit interface"
font_size: 60
color: app.theme_cls.primary_dark
GridLayout:
cols: 2
Button:
text: "Start"
on_release:
app.root.current = "CommandScreen"
Button:
text: "Settings"

View File

View File

@@ -0,0 +1,3 @@
import microbit
microbit.uart.write()

View File

@@ -0,0 +1,48 @@
import bin.com.csv_parsers
cvr = bin.com.csv_parsers.CsvRead()
class AutoComplete:
def __init__(self):
self.__length_command_completion = 0
self.__command_list = []
self.__possible_completion = []
self.item = ""
self.items = ""
self.__return_value = []
self.__return_value_assembly = ""
self.__command_count = 0
self.text = ""
def autocomplete(self, text, command_list_path):
self.text = str(text).lower()
if self.text[len(self.text) - 2:] == "\t\n":
self.text = self.text[:len(self.text) - 2]
elif self.text[len(self.text) - 1:] == "\t":
self.text = self.text[:len(self.text) - 1]
self.__command_list = cvr.importing(command_list_path).pop(0)
self.__return_value = []
self.__return_value_assembly = ""
self.__possible_completion = []
self.__command_count = 0
for self.item in self.__command_list:
if self.text == self.item[:len(self.text)]:
self.__possible_completion.append(self.item)
else:
pass
if len(self.__possible_completion) < 1:
self.__return_value = [f"{self.text}\n-micro:bit - No such command", self.text[:len(self.text)]]
elif len(self.__possible_completion) == 1:
self.__return_value = ["", str(self.__possible_completion.pop(0))]
else:
for self.items in self.__possible_completion:
self.__return_value_assembly += f"{str(self.items)} "
if self.__command_count > 2:
self.__return_value_assembly += "\n"
self.__command_count = 0
else:
self.__command_count += 1
self.__return_value.append(self.__return_value_assembly)
self.__return_value.append(self.text[:len(self.text)])
return self.__return_value

26
bin/others/run_command.py Normal file
View File

@@ -0,0 +1,26 @@
import serial
import bin.com.csv_parsers
import bin.com.lib
cvr = bin.com.csv_parsers.CsvRead()
com = bin.com.lib.Com()
class RunCommand:
def __init__(self):
self.__all_commands = []
self.__return = ""
def runcommand(self, command, command_list_path):
self.__all_commands = cvr.importing(command_list_path).pop(0)
if command in self.__all_commands:
try:
com.connect(19200, "")
com.send(command)
self.__return = "The command executed successfully"
except serial.SerialException:
self.__return = f"[micro:bit - {command}]: An error occurred running the command. (Maybe disconnected or no permission?)"
else:
self.__return = "-micro:bit - No such command"
return self.__return

7
config/settings.ini Normal file
View File

@@ -0,0 +1,7 @@
[Info]
version = V0.1
subVersion = -dev1
[Dev Settings]
log_level = DEBUG

176
main.py Normal file
View File

@@ -0,0 +1,176 @@
#########################################################
"""@package docstring
Micro:bit Bluetooth Interface, developed by simplePCBuilding, alpha 1.0
This App allows you to connect to a micro:bit via the USB cable and as such transmit to
and recieve Data from it. This file here is the control file for the UI and as such
should not be interfaced with. All the api files are located in the bin directory."""
#########################################################
# IMPORTS
import logging
import os
import configparser
import datetime
from kivymd.uix.screen import MDScreen
from kivymd.app import MDApp
from kivy.uix.screenmanager import ScreenManager
from kivy.base import Builder
from kivy.uix.popup import Popup
from kivy.clock import Clock
from kivy.core.window import Window
import bin.others.autocomplete
import bin.others.run_command
################################
# VARIABLE SETUP
################
config = configparser.ConfigParser()
config.read('./config/settings.ini')
version_app = f"{config['Info']['version']}{config['Info']['subVersion']}"
ac = bin.others.autocomplete.AutoComplete()
rc = bin.others.run_command.RunCommand()
################################
################################
# LOGGER SETUP
##############
# BASIC SETUP
logging.basicConfig(level=logging.DEBUG, filename="./log/main_log.log", filemode="w")
logs = f"./log/{datetime.datetime.now()}-log-main.log"
logger = logging.getLogger(__name__)
# SETUP OF HANDLER & FORMATTER
handler = logging.FileHandler(logs)
formatter = logging.Formatter("%(levelname)s - %(asctime)s - %(name)s: %(message)s -- %(lineno)d")
handler.setFormatter(formatter)
logger.addHandler(handler)
# FINAL CONFIG & FIRST LOG ENTRY
logger.setLevel(config['Dev Settings']['log_level'])
logger.info(f"Logger initialized, app is running Version: {version_app}")
################################
################################
# SETTINGS HANDLER
##################
class SettingsHandler:
def __init__(self):
pass
def settings_handler(self):
pass
################################
##############################################################
# SCREENS
##############################################################
################################
# LOAD SCREEN
#############
class Load(MDScreen):
def start_pb(self):
self.ids.progress.start()
################################
################################
# MAIN SCREEN
#############
class Main(MDScreen):
pass
################################
################################
# COMMAND SCREEN
################
class Command(MDScreen):
def autocomplete(self):
self.text = self.ids.tin.text
self.input = self.text[len(self.text) - 1:]
if self.input == "\t":
self.__ac = ac.autocomplete(self.text, "./bin/data/full_command_list.csv")
self.__history = self.ids.cmd_out.text
self.__output_text = self.__history + "\n\n" + str(self.__ac.pop(0))
self.ids.cmd_out.text = self.__output_text
self.ids.tin.text = self.__ac.pop(0)
else:
pass
def runcommand(self):
self.__info = rc.runcommand(self.ids.tin.text, "./bin/data/full_command_list.csv")
if self.__info == "The command executed successfully":
logger.debug(f"The following command has been run successfully: {self.ids.tin.text}")
else:
logger.debug(f"The following command has failed to run: {self.ids.tin.text}")
self.ids.tin.text = ""
self.__history = self.ids.cmd_out.text
self.__output_text = self.__history + "\n\n" + str(self.__info)
self.ids.cmd_out.text = self.__output_text
################################
####################################################
################################
# SCREEN MANAGER
################
class RootScreen(ScreenManager):
pass
################################
# UI MANAGER
################################
class MicrobitInterface(MDApp):
global screen_manager
screen_manager = ScreenManager()
logger.info("building app...")
def build(self):
self.title = f"Microbit Bluetooth Interface {version_app}"
self.theme_cls.primary_palette = "Blue"
self.theme_cls.accent_palette = "BlueGray"
# self.icon =
screen_manager.add_widget(Builder.load_file("./bin/gui/load_screen.kv"))
screen_manager.add_widget(Builder.load_file("./bin/gui/main_screen.kv"))
screen_manager.add_widget(Builder.load_file("./bin/gui/command_screen.kv"))
return screen_manager
# Redirect start instructions to switch screen
def on_start(self):
logger.info("App is starting")
Clock.schedule_once(self.launch_app, 0.5)
# Switching screens after init
def launch_app(self, dt):
screen_manager.current = "HomeScreen"
screen_manager.transition.duration = 0.2
screen_manager.transition.direction = "left"
logger.info("App start successful")
MicrobitInterface().run()

3
testfile.py Normal file
View File

@@ -0,0 +1,3 @@
import bin.others.autocomplete
print(bin.others.autocomplete.AutoComplete().autocomplete("Sta\t\n", "./bin/data/full_command_list.csv"))