Add files via upload

This commit is contained in:
Seo Chaegeon authored and GitHub committed 2026-01-08 16:05:05 +09:00
1 parent 875dc44682
commit e3bb87d4e7
15 files changed
+3623 -1

No files matched your search

+59 -1
View File
@@ -1 +1,59 @@
# Smartfactory <img width="1260" height="891" alt="Image" src="https://github.com/user-attachments/assets/600a07b7-64cd-4822-b122-ef01c0ea5cd6" />
# Intel Geti + AI QC Conveyor
인텔 Geti + 인공지능 QC 컨베이어
## 프로젝트 소개
인건비 절감을 위한 생산 공장 컨베이어 자동화 장치 입니다.
<br>
## 개발 기간
* 25.09.24 - 25.10.22
### 맴버구성
| 이름 | 담당 |
|------|------|
|서채건|PM, GUI 구성, 코드 통합|
|김동현|코드 통합, PPT제작, 발표|
|김선곤|GUI 구성, DB 읽기 쓰기 로직|
|박서정|데이터셋 학습 및 검증, 스티커 유무 감별|
|조민재|데이터셋 학습 및 검증, 오염 비율 계산|
### 개발 환경
- `Intel Geti`
- `Python 3.12.3`
- `Arduino Mega`
- `MariaDB 10`
- `Tkinter`
## 주요 기능
#### 모델 1 작동
- Pink 병뚜껑 QC 스티커 유무, 오염 유무
- No 스티커 or 오염 30% 초과 or Purple 병뚜껑 -> 완전불량
- 스티커 and 오염 0% 초과 30% 이하 -> 부분불량
- 스티커 and 오염 0% -> 정상
#### 모델 2 작동
- Purple 병뚜껑 QC 스티커 유무, 오염 유무
- No 스티커 or 오염 30% 초과 or Pink 병뚜껑 -> 완전불량
- 스티커 and 오염 0% 초과 30% 이하 -> 부분불량
- 스티커 and 오염 0% -> 정상
#### 관리자 모드
- 수동 컨베이어 제어
- 수동 엑츄에이터 조작
## 파일 구조
```
team2/
├─ iotdemo/
│ ├─ __init__.py
│ ├─ debounce.py
│ ├─ factory_controller.py
│ ├─ pins.py
│ ├─ pyduino.py
│ └─ pyft232.py
├─ MODEL_FILE
└─ run.py
```
+148
View File
@@ -0,0 +1,148 @@
/* Packet Processing */
#define PACKET_START (0x55)
#define PACKET_END (0xaa)
#define PACKET_ID_INTERRUPT (0x00)
#define PACKET_ID_MAX (199)
#define PACKET_ID_ERROR (0xff)
static byte packet[5] = { PACKET_START, 0xff, 0xff, 0xff, PACKET_END };
enum pin_functions
{
BEACON_RED = 2,
BEACON_ORANGE,
BEACON_GREEN,
BEACON_BUZZER,
ACTUATOR_1, // 6
ACTUATOR_2,
CONVEYOR_EN,
CONVEYOR_PWM,
START_BUTTON, // 10
STOP_BUTTON,
PHOTOELECTRIC_SENSOR_1,
PHOTOELECTRIC_SENSOR_2,
LED // 14
};
#define SEND_PACKET(pin, value) \
do \
{ \
packet[2] = (pin); \
packet[3] = (value); \
Serial.write(packet, sizeof(packet)); \
} while (0)
/* Utility Functions */
#define INIT_OUTPUT_PAD(pad, val) \
pinMode((pad), OUTPUT); \
digitalWrite((pad), (val))
#define INIT_PWM_PAD(pad, val) \
pinMode((pad), OUTPUT); \
analogWrite((pad), (val))
#define CHECK(expr) \
if ((expr)) \
{ \
SEND_PACKET(0xff, 0xff); \
return; \
}
/* System Initialization */
void setup()
{
/* Initialize Outputs */
INIT_OUTPUT_PAD(BEACON_RED, HIGH);
INIT_OUTPUT_PAD(BEACON_ORANGE, HIGH);
INIT_OUTPUT_PAD(BEACON_GREEN, HIGH);
INIT_OUTPUT_PAD(BEACON_BUZZER, HIGH);
INIT_OUTPUT_PAD(ACTUATOR_1, HIGH);
INIT_OUTPUT_PAD(ACTUATOR_2, HIGH);
INIT_OUTPUT_PAD(CONVEYOR_EN, HIGH);
INIT_PWM_PAD(CONVEYOR_PWM, 0);
INIT_OUTPUT_PAD(LED, HIGH);
/* Initialize Inputs */
pinMode(START_BUTTON, INPUT_PULLUP);
pinMode(STOP_BUTTON, INPUT_PULLUP);
pinMode(PHOTOELECTRIC_SENSOR_1, INPUT_PULLUP);
pinMode(PHOTOELECTRIC_SENSOR_2, INPUT_PULLUP);
/* Prepare UART port - 115200 baud rate */
Serial.begin(115200);
while (!Serial)
;
}
/* Set to 0xff to report initial state */
static byte inputStatus[4] = { 0xff, 0xff, 0xff, 0xff };
static inline compareAndReport(byte pin)
{
static byte idx;
static byte value;
idx = pin - START_BUTTON;
value = digitalRead(pin);
if (inputStatus[idx] == value)
return;
inputStatus[idx] = value;
SEND_PACKET(pin, value);
}
static inline void polling(void)
{
packet[1] = PACKET_ID_INTERRUPT;
compareAndReport(START_BUTTON);
compareAndReport(STOP_BUTTON);
compareAndReport(PHOTOELECTRIC_SENSOR_1);
compareAndReport(PHOTOELECTRIC_SENSOR_2);
}
/* Actual Main loop */
void loop()
{
polling();
/* Packet Processing */
if (Serial.available() < 5)
return;
static byte packetId;
static byte pin;
static byte value;
packet[1] = PACKET_ID_ERROR;
CHECK(Serial.read() != PACKET_START);
packetId = Serial.read();
CHECK(packetId == 0 || packetId > PACKET_ID_MAX);
pin = Serial.read();
CHECK(pin < BEACON_RED || pin > LED);
value = Serial.read();
CHECK(Serial.read() != PACKET_END);
/* Run command */
packet[1] = packetId;
if (pin == CONVEYOR_PWM)
analogWrite(pin, value);
else
digitalWrite(pin, !!value);
SEND_PACKET(pin, value);
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 705 KiB

Binary file not shown.
+10
View File
@@ -0,0 +1,10 @@
"""
Smart Factory controller APIs
"""
from .factory_controller import FactoryController
from .pins import Inputs, Outputs
from .pyduino import PyDuino
from .pyft232 import PyFt232
__all__ = ('FactoryController', 'Inputs', 'Outputs', 'PyDuino', 'PyFt232')
+164
View File
@@ -0,0 +1,164 @@
"""
Robust debounce decorator for callbacks like GPIO interrupts.
Features:
- Leading / trailing edge control
- Optional max_wait (force-fire) to avoid indefinite deferral
- Per-key debounce (e.g., per-pin) for shared handlers
- Thread-safe (uses Lock), time.monotonic() for steady-time
- Drop-in compatible with previous debounce(wait_sec) usage
"""
from __future__ import annotations
import time
import threading
from functools import wraps
from typing import Any, Callable, Optional
def debounce(
wait_sec: float,
*,
leading: bool = False,
trailing: bool = True,
max_wait: Optional[float] = None,
key: Optional[Callable[..., Any]] = None,
clock: Callable[[], float] = time.monotonic,
):
"""
Debounce a function so that it’s not called too frequently.
Args:
wait_sec: Quiet period length. Calls within this window are coalesced.
leading: If True, call immediately on the first call in a burst.
trailing: If True, call after the burst “settles” for wait_sec.
max_wait: Force a call if this many seconds have elapsed since the
last *invocation* (helps for steady streams).
key: Per-key debounce. A function that maps (*args, **kwargs) to a
hashable key (e.g., lambda pin, *_: pin). If None, a single
global bucket is used.
clock: Time source; default is time.monotonic().
Notes:
- If both leading and trailing are True:
* first call in a burst fires immediately,
* further calls delay a trailing fire until wait_sec after the last call,
* but a trailing fire is skipped if there were no calls after the leading fire.
- If only leading is True => at most one call per burst, immediate.
- If only trailing is True => only the last call args are used.
- If max_wait is set, a call is forced at most every max_wait seconds.
"""
if not leading and not trailing:
raise ValueError("At least one of leading or trailing must be True.")
class _Bucket:
__slots__ = (
"lock", "timer", "last_invoke", "last_call",
"pending", "pending_args", "pending_kwargs",
)
def __init__(self):
self.lock = threading.Lock()
self.timer: Optional[threading.Timer] = None
self.last_invoke: Optional[float] = None # last actual function call time
self.last_call: Optional[float] = None # last wrapper call time
self.pending: bool = False
self.pending_args = ()
self.pending_kwargs = {}
buckets = {}
buckets_lock = threading.Lock()
def _get_bucket(k: Any) -> _Bucket:
with buckets_lock:
b = buckets.get(k)
if b is None:
b = _Bucket()
buckets[k] = b
return b
def decorator(fn: Callable):
@wraps(fn)
def wrapper(*args, **kwargs):
# derive bucket key
k = key(*args, **kwargs) if key else None
b = _get_bucket(k)
now = clock()
def _invoke_locked():
"""Call the function with the latest pending args/kwargs.
b.lock must be held by caller."""
b.last_invoke = clock()
b.pending = False
call_args = b.pending_args
call_kwargs = b.pending_kwargs
# release the lock during the user call to avoid deadlocks
b.lock.release()
try:
return fn(*call_args, **call_kwargs)
finally:
b.lock.acquire()
def _schedule_trailing():
"""(Re)schedule trailing call timer from 'now'."""
if b.timer:
b.timer.cancel()
b.timer = threading.Timer(wait_sec, _trailing_fire)
b.timer.daemon = True
b.timer.start()
def _trailing_fire():
with b.lock:
# fire only if something is pending
if not b.pending:
return
# if max_wait is set and we haven't exceeded it yet AND there
# were no new calls, still trailing should run because timer fired
_invoke_locked()
# after trailing fire, clear timer
t = b.timer
b.timer = None
if t:
try:
t.cancel()
except Exception:
pass
with b.lock:
b.last_call = now
b.pending = True
b.pending_args = args
b.pending_kwargs = kwargs
call_now = False
# Decide leading fire
if leading:
if b.last_invoke is None:
# first ever
call_now = True
else:
# if enough time has passed since last_invoke -> new burst
if (now - b.last_invoke) >= wait_sec and (b.timer is None):
call_now = True
# Decide max_wait forced fire (never conflict with call_now)
if (not call_now) and max_wait is not None and b.last_invoke is not None:
if (now - b.last_invoke) >= max_wait:
call_now = True
result = None
if call_now:
result = _invoke_locked()
# after a leading/max_wait fire, we may still want trailing if more
# calls keep arriving; re-arm the trailing timer if trailing is True
if trailing:
_schedule_trailing()
else:
# no immediate call; manage trailing
if trailing:
_schedule_trailing()
return result
return wrapper
return decorator
+250
View File
@@ -0,0 +1,250 @@
"""
Smart Factory HW module controller
"""
from logging import getLogger
from os import listdir
from sys import platform
from time import sleep
from typing import Optional, Union
from .debounce import debounce
from .pins import Inputs, Outputs
from .pyduino import PyDuino
from .pyft232 import PyFt232
__all__ = ('FactoryController', )
class FactoryController:
"""
Factory HW controller class
"""
DEV_ON = False
DEV_OFF = True
def __init__(self,
port: Optional[Union[str, int]] = None,
*,
debug: bool = True):
self.debug = debug
if self.debug:
self.logger = getLogger('CONTROLLER')
self.__force_stop = False
self.__device = None
self.__device_name = None
# open device
self.port = self.__detect_serial(port if port is not None else -1)
if self.port is not None and 'ttyUSB' in self.port:
try:
self.__device = PyFt232(self.port, debug=debug)
self.__device_name = 'ft232'
except Exception:
self.__device = None
else:
try:
self.__device = PyDuino(self.port, debug=debug)
self.__device_name = 'arduino'
except Exception:
self.__device = None
# Arduino beacon indicator
self.red = False
self.orange = False
self.green = False
self.led = False
self.defect_sensor_status = False
self.color_sensor_status = False
# interrupt handler
if not self.is_dummy:
self.__device.watch(Inputs.START_BUTTON,
self.__button_interrupt)
self.__device.watch(Inputs.STOP_BUTTON,
self.__button_interrupt)
self.__device.watch(Inputs.PHOTOELECTRIC_SENSOR_1,
self.__sensor_interrupt)
self.__device.watch(Inputs.PHOTOELECTRIC_SENSOR_2,
self.__sensor_interrupt)
if self.debug:
self.logger.info(
f'use {"Dummy" if self.is_dummy else "Arduino"} Controller')
def __detect_serial(self, port):
if isinstance(port, str):
return port
if not isinstance(port, int):
raise RuntimeError(
f'Invalid port argument type: {port} - {type(port)}')
if platform in {'win32', 'cygwin'}:
return f'COM{port}'
if not platform.startswith('linux'):
raise RuntimeError('Not supported OS')
if port == -1:
for path in listdir("/dev"):
if path.startswith("ttyACM"):
return f"/dev/{path}"
else:
return f'/dev/ttyACM{port}'
return None
def __del__(self):
self.close()
def __enter__(self):
if not self.is_dummy:
self.system_start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if not self.is_dummy:
self.system_stop()
self.close()
def __set(self, pin, value):
if self.is_dummy or self.__force_stop:
return
self.__device.set(pin, value)
def __get(self, pin):
if self.is_dummy:
return None
return self.__device.get(pin)
def __led(self, pin, on):
if self.__device_name == 'arduino':
self.__set(
pin,
FactoryController.DEV_ON if on else FactoryController.DEV_OFF)
def __actuator(self, actuator_id):
if self.__device_name == 'arduino':
self.__set(actuator_id, FactoryController.DEV_ON)
sleep(0.1)
self.__set(actuator_id, FactoryController.DEV_OFF)
elif self.__device_name == 'ft232':
self.__set(PyFt232.PKT_CMD_DETECTION, actuator_id)
sleep(0.5)
self.__set(PyFt232.PKT_CMD_DETECTION, PyFt232.PKT_CMD_DETECTION_0)
@debounce(0.1)
def __button_interrupt(self, pin: int, value: int, *_):
if value != 0 or self.__force_stop:
return
if pin == Inputs.START_BUTTON:
self.system_start()
elif pin == Inputs.STOP_BUTTON:
self.system_stop()
@debounce(0.3)
def __sensor_interrupt(self, pin: int, value: int, *_):
status = value == 0
if pin == Inputs.PHOTOELECTRIC_SENSOR_1:
self.defect_sensor_status = status
elif pin == Inputs.PHOTOELECTRIC_SENSOR_2:
self.color_sensor_status = status
###########################################################################
# Public properties
@property
def is_dummy(self):
return self.__device is None
@property
def red(self):
return bool(self.__get(Outputs.BEACON_RED))
@red.setter
def red(self, on):
self.__led(Outputs.BEACON_RED, on)
@property
def orange(self):
return bool(self.__get(Outputs.BEACON_ORANGE))
@orange.setter
def orange(self, on):
self.__led(Outputs.BEACON_ORANGE, on)
@property
def green(self):
return bool(self.__get(Outputs.BEACON_GREEN))
@green.setter
def green(self, on):
self.__led(Outputs.BEACON_GREEN, on)
@property
def led(self):
return bool(self.__get(Outputs.LED))
@led.setter
def led(self, on):
self.__led(Outputs.LED, on)
@property
def conveyor(self):
return bool(self.__get(Outputs.CONVEYOR_EN))
@conveyor.setter
def conveyor(self, on):
if on:
self.__set(Outputs.CONVEYOR_EN, FactoryController.DEV_ON)
self.__set(Outputs.CONVEYOR_PWM, 255)
else:
self.__set(Outputs.CONVEYOR_PWM, 0)
self.__set(Outputs.CONVEYOR_EN, FactoryController.DEV_OFF)
def push_actuator(self, num):
if self.__device_name == 'arduino':
if num == 1:
self.__actuator(Outputs.ACTUATOR_1)
else:
self.__actuator(Outputs.ACTUATOR_2)
elif self.__device_name == 'ft232':
self.__actuator(num)
###########################################################################
# Public methods
def system_start(self) -> None:
if self.debug:
self.logger.info("Start System")
self.conveyor = False
def system_stop(self) -> None:
if self.debug:
self.logger.info("Stop System")
self.conveyor = True
def close(self):
if self.debug:
self.logger.info("Shutdown System")
self.__force_stop = True
if self.is_dummy:
return
self.__device.close()
+27
View File
@@ -0,0 +1,27 @@
"""
Input / Output Pin map for the smart factory demo kit
"""
from enum import IntEnum
class Outputs(IntEnum):
BEACON_RED = 2
BEACON_ORANGE = 3
BEACON_GREEN = 4
BEACON_BUZZER = 5
ACTUATOR_1 = 6
ACTUATOR_2 = 7
CONVEYOR_EN = 8
CONVEYOR_PWM = 9
LED = 14
class Inputs(IntEnum):
START_BUTTON = 10
STOP_BUTTON = 11
PHOTOELECTRIC_SENSOR_1 = 12
PHOTOELECTRIC_SENSOR_2 = 13
+294
View File
@@ -0,0 +1,294 @@
"""
Raw level arduino GPIO controll module
"""
from logging import getLogger
from queue import Queue
from threading import Condition, Lock, Thread
from typing import Callable, Optional
from serial import Serial
__all__ = ('PyDuino', )
def _dummy(*_):
pass
class PyDuino:
"""
Raw level arduino GPIO controll class
"""
PACKET_START = 0x55
PACKET_END = 0xaa
PACKET_ID_INTERRUPT = 0
PACKET_ID_MAX = 199
PACKET_ID_ERROR = 0xff
def __init__(self,
port: str,
baudrate: int = 115200,
*,
debug: bool = False):
self.debug = debug
if self.debug:
self.logger = getLogger('PYDUINO')
self.logger.info("serial port %s (%d)", port, baudrate)
self.__arduino = None
self.__stop_requested = False # to support graceful exit
self.__force_stop = False
self.__watcher = {}
self.__values = [1] * 70 # Pin status
for idx in (0, 1):
self.__values[idx] = 0xff # NC
# init arduio
arduino = Serial(port=port, baudrate=baudrate, timeout=1)
arduino.reset_input_buffer()
arduino.reset_output_buffer()
self.__arduino = arduino
self.__init_cv = Condition()
self.__init_input_pins = (10, 11, 12, 13)
self.__init_count = 0
self.__rx = Thread(target=self.__receiver)
self.__rx.name = 'Arduino Rx'
self.__rx.start()
self.__init()
self.__tx_lock = Lock()
self.__tx_cv = Condition()
self.__tx_queue = Queue()
self.__tx_packet_id = 1
self.__tx = Thread(target=self.__transmitter)
self.__tx.name = 'Arduino Tx'
self.__tx.start()
def __del__(self):
self.close()
def __wait_for_input_status(self, pin: int, value: int, _):
self.__values[pin] = value
with self.__init_cv:
self.__init_count += 1
if self.__init_count == len(self.__init_input_pins):
self.__init_cv.notify()
def __init(self):
with self.__init_cv:
self.__init_cv.wait()
for idx in self.__init_input_pins:
self.watch(idx)
# pyduino.py - inside class PyDuino
def __transmitter(self):
arduino = self.__arduino
while True:
packet = self.__tx_queue.get()
if self.debug:
self.logger.info(packet)
pin, value, packet_id = packet
self.__tx_queue.task_done()
if pin is None:
break # End of Tx
pin = int(pin) & 0xff
value = int(value) & 0xff
try:
arduino.write(bytes([
PyDuino.PACKET_START, packet_id, pin, value,
PyDuino.PACKET_END
]))
except Exception as e:
# 치명 오류 시: 강제 정지하고 Tx 종료
try:
from serial import SerialException
except Exception:
SerialException = Exception
if isinstance(e, (SerialException, OSError)):
if self.debug:
try: self.logger.warning("Tx write failed / serial down: %s", e)
except Exception: pass
self.__force_stop = True
# set()에서 wait중일 수 있으니 깨워준다
try:
with self.__tx_cv:
self.__tx_cv.notify_all()
except Exception:
pass
break
# 기타 오류는 약간 쉬고 재시도(선택)
import time; time.sleep(0.02)
# Rx Thread
def __receiver(self):
import time
# 초기 입력핀 감시 등록(기존 로직 유지)
for idx in self.__init_input_pins:
self.__values[idx] = 0xff
self.watch(idx, self.__wait_for_input_status)
while self.__force_stop is False:
try:
packet = self.__receive_packet()
except Exception as e:
# 시리얼 분리/중복접근 등 치명 오류 → 1회 로깅 후 조용히 종료
try:
from serial import SerialException
except Exception:
SerialException = Exception
if isinstance(e, (SerialException, OSError)):
if self.debug:
try: self.logger.warning("Serial disconnected/busy: %s", e)
except Exception: pass
# 상태 플래그(옵션)
try: self._connected = False
except Exception: pass
break
# 기타 오류는 소프트 리트라이
time.sleep(0.02)
continue
if packet is None:
# 타임아웃/빈 읽기 → 살짝 쉬고 계속
time.sleep(0.02)
continue
# 정상 패킷 처리
pin, value, packet_id = packet
self.__values[pin] = value
if packet_id == PyDuino.PACKET_ID_INTERRUPT:
self.__watcher.get(pin, _dummy)(*packet)
else:
with self.__tx_cv:
self.__tx_cv.notify()
def __receive_packet(self):
arduino = self.__arduino
# 안전 타임아웃 보정(0/None이면 짧게 설정)
try:
if getattr(arduino, "timeout", None) in (None, 0):
arduino.timeout = 0.2
except Exception:
pass
# 첫 바이트 읽기
try:
data = arduino.read()
except Exception:
# 상위에서 예외 처리
raise
if not data or data[0] != PyDuino.PACKET_START:
return None
# 나머지 4바이트 조립(타임아웃이면 None 반환)
packet = []
while len(packet) != 4:
try:
data = arduino.read()
except Exception:
raise
if not data:
return None # Timeout → 상위 루프가 부드럽게 스킵
packet.append(data[0])
packet_id, pin, value, packet_end = packet
if packet_id < 0 or packet_id > PyDuino.PACKET_ID_ERROR:
return None
if pin < 0 or pin > len(self.__values):
return None
if packet_end != PyDuino.PACKET_END:
return None
return (pin, value, packet_id)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
def close(self) -> None:
"""
Close existing arduino connection
"""
if self.__arduino is None:
return
self.__stop_requested = True
if self.__force_stop:
return
# post end of tx marker
self.__tx_queue.put((None, None, None))
self.__tx.join()
# Stop Rx
self.__force_stop = True
self.__rx.join()
# close serial
self.__arduino.close()
if self.debug:
self.logger.info("stopped")
# pyduino.py - inside class PyDuino
def set(self, pin: int, value: int) -> bool:
"""
Set arduino pin with given value
"""
if self.__stop_requested or self.__force_stop:
return False
with self.__tx_lock:
packet_id = self.__tx_packet_id
ok = False
with self.__tx_cv:
self.__tx_queue.put((pin, value, packet_id))
# wait()에 타임아웃 추가 (예: 0.5s)
self.__tx_cv.wait(timeout=0.5)
ok = not self.__force_stop # Rx/Tx가 죽었으면 False
self.__tx_packet_id += 1
if self.__tx_packet_id == PyDuino.PACKET_ID_MAX:
self.__tx_packet_id = 1
return ok
def get(self, pin: int) -> int:
"""
Get current arduino pin value from proxy
"""
return self.__values[pin]
def watch(
self,
pin: int,
callback: Optional[Callable[[int, int, int],
None]] = None) -> None:
"""
Set callback function for given pin
"""
if callback is None:
callback = _dummy
self.__watcher[pin] = callback
+213
View File
@@ -0,0 +1,213 @@
"""
Raw level FT232 controll module
"""
from logging import getLogger
from queue import Queue
from threading import Condition, Lock, Thread
from typing import Callable, Optional
from serial import Serial
__all__ = ('PyFt232', )
def _dummy(*_):
pass
class PyFt232:
"""
Raw level FT232 controll class
"""
PKT_START = 0xaa
PKT_END = 0xaa
PKT_CMD_ACK = 0x00
PKT_CMD_START = 0x01
PKT_CMD_START_STOP = 0x00
PKT_CMD_START_START = 0x01
PKT_CMD_LOADING = 0x02
PKT_CMD_LOADING_STOP = 0x00
PKT_CMD_LOADING_LOAD = 0x01
PKT_CMD_DETECTION = 0x03
PKT_CMD_DETECTION_0 = 0x00
PKT_CMD_DETECTION_1 = 0x01
PKT_CMD_DETECTION_2 = 0x02
PKT_CMD_DETECTION_3 = 0x03
PKT_CMD_SPEED = 0x04
PKT_CMD_SPEED_STOP = 0x00
PKT_CMD_SPEED_UP = 0x01
PKT_CMD_SPEED_DOWN = 0x02
#PKT_CMD_AUTO_LOADING = 0x05
#PKT_CMD_VERSION = 0x64
PKT_ID_MAX = 199
PKT_ID_ERROR = 0xff
def __init__(self,
port: str = '/dev/ttyUSB0',
baudrate: int = 19200,
*,
debug: bool = False):
self.debug = debug
if self.debug:
self.logger = getLogger('PYFT232')
self.logger.info("serial port %s (%d)", port, baudrate)
self.__ft232 = None
self.__stop_requested = False # to support graceful exit
self.__force_stop = False
self.__values = [1] * 14 # Pin status
for idx in (0, 1):
self.__values[idx] = 0xff # NC
# init arduio
ft232 = Serial(port=port, baudrate=baudrate, timeout=1)
ft232.reset_input_buffer()
ft232.reset_output_buffer()
self.__ft232 = ft232
self.__rx = Thread(target=self.__receiver)
self.__rx.name = 'Arduino Rx'
self.__rx.start()
self.__init()
self.__tx_lock = Lock()
self.__tx_cv = Condition()
self.__tx_queue = Queue()
self.__tx = Thread(target=self.__transmitter)
self.__tx.name = 'Arduino Tx'
self.__tx.start()
def __del__(self):
self.close()
def __checksum(self, length: int, cmd: int, data: list):
return ~((length + cmd + sum(data)) & 0xff) & 0xff
def __init(self):
pass
# Tx Thread
def __transmitter(self):
ft232 = self.__ft232
while True:
packet = self.__tx_queue.get()
if self.debug:
self.logger.info(packet)
if len(packet) != 2:
break
cmd, data = packet
self.__tx_queue.task_done()
if cmd is None:
break # End of Tx
ft232.write(
bytes([
PyFt232.PKT_START, 0x05, cmd, data, self.__checksum(0x05, cmd, [data])
]))
# Rx Thread
def __receiver(self):
while self.__force_stop is False:
packet = self.__receive_packet()
if self.debug:
self.logger.info(packet)
if packet is None:
continue
# TODO : What need to be done?
cmd, data = packet
# run callback
with self.__tx_cv:
self.__tx_cv.notify()
def __receive_packet(self):
ft232 = self.__ft232
data = ft232.read()
if len(data) == 0 or data[0] != PyFt232.PKT_START:
return None
packet = []
while len(packet) != 5:
data = ft232.read()
if len(data) == 0:
return None # Timeout
packet.append(data[0])
length, cmd, data, dataerr, checksum = packet
if length != 6:
return None
if cmd != 0x00 or dataerr != 0x00:
return None
if self.__checksum(length, cmd, [data, dataerr]) != checksum:
return None
return (cmd, data)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
def close(self) -> None:
"""
Close existing ft232 connection
"""
if self.__ft232 is None:
return
self.__stop_requested = True
if self.__force_stop:
return
# post end of tx marker
self.__tx_queue.put((None, None, None))
self.__tx.join()
# Stop Rx
self.__force_stop = True
self.__rx.join()
# close serial
self.__ft232.close()
if self.debug:
self.logger.info("stopped")
def set(self, cmd: int, value: int) -> bool:
"""
Set ft232 pin with given value
"""
if self.__stop_requested:
return False
with self.__tx_lock:
with self.__tx_cv:
self.__tx_queue.put((cmd, value))
self.__tx_cv.wait()
return True
def get(self, pin: int) -> int:
"""
Get current ft232 pin value from proxy
"""
return self.__values[pin]
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

+6
View File
@@ -0,0 +1,6 @@
pymysql
opencv-python
numpy
onnxruntime
tk
pillow
+2452
View File
File diff suppressed because it is too large. Load diff