Доработка сборки проекта
This commit is contained in:
105
README.md
105
README.md
@@ -1,16 +1,101 @@
|
|||||||
# GPIO Monitor Server
|
# GPIO Monitor Dashboard for Raspberry Pi
|
||||||
|
|
||||||
Сервер для мониторинга GPIO на Raspberry Pi.
|
Реалтайм мониторинг параллельной GPIO-шины Raspberry Pi через C + Go + Web Dashboard.
|
||||||
|
|
||||||
## Структура проекта
|
Проект читает входящие сигналы с GPIO-пинов Raspberry Pi по прерыванию, передаёт данные через FIFO pipe в Go-сервер и отображает всё в браузере в реальном времени.
|
||||||
- `cmd/server/` - точка входа приложения
|
|
||||||
- `internal/` - внутренняя логика
|
---
|
||||||
- `web/` - веб-интерфейс
|
|
||||||
- `scripts/` - вспомогательные скрипты
|
# Возможности
|
||||||
|
|
||||||
|
✅ Захват данных по GPIO interrupt
|
||||||
|
✅ Работа через WiringPi
|
||||||
|
✅ FIFO pipe между C и Go
|
||||||
|
✅ Высокоскоростной RingBuffer
|
||||||
|
✅ HTTP API
|
||||||
|
✅ Web Dashboard
|
||||||
|
✅ Работа через Wi-Fi Access Point Raspberry Pi
|
||||||
|
✅ Подходит для ПК и мобильных устройств
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Архитектура
|
||||||
|
|
||||||
|
```text
|
||||||
|
GPIO BUS (8 bit)
|
||||||
|
↓
|
||||||
|
gpio-interrupt.c
|
||||||
|
↓ stdout
|
||||||
|
systemd socket FIFO (/tmp/gpio_pipe)
|
||||||
|
↓
|
||||||
|
Go Server
|
||||||
|
↓
|
||||||
|
HTTP API (:8080)
|
||||||
|
↓
|
||||||
|
Dashboard (Browser)
|
||||||
|
|
||||||
|
# Структура проекта
|
||||||
|
|
||||||
|
|
||||||
|
.
|
||||||
|
├── cmd/server/main.go # Go HTTP сервер
|
||||||
|
├── internal/adapter/ # RingBuffer
|
||||||
|
├── web/
|
||||||
|
│ ├── index.html
|
||||||
|
│ ├── dashboard.html
|
||||||
|
│ ├── css/style.css
|
||||||
|
│ ├── js/app.js
|
||||||
|
│ └── favicon.png
|
||||||
|
├── scripts/
|
||||||
|
├── start-server.sh
|
||||||
|
└── README.md
|
||||||
|
|
||||||
|
# GPIO pin
|
||||||
|
|
||||||
|
WR/STROBE = GPIO27
|
||||||
|
|
||||||
|
DATA BUS:
|
||||||
|
D0 = GPIO21
|
||||||
|
D1 = GPIO7
|
||||||
|
D2 = GPIO6
|
||||||
|
D3 = GPIO5
|
||||||
|
D4 = GPIO25
|
||||||
|
D5 = GPIO24
|
||||||
|
D6 = GPIO23
|
||||||
|
D7 = GPIO22
|
||||||
|
|
||||||
|
# Установка
|
||||||
|
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install golang git build-essential
|
||||||
|
|
||||||
|
# Сборка
|
||||||
|
|
||||||
## Сборка
|
|
||||||
go build -o gpio-monitor-server ./cmd/server
|
go build -o gpio-monitor-server ./cmd/server
|
||||||
|
|
||||||
## Запуск
|
# Запуск сервера
|
||||||
./gpio-monitor-server
|
|
||||||
|
|
||||||
|
./start-server.sh
|
||||||
|
|
||||||
|
sudo systemctl start monitor-gpio.service
|
||||||
|
|
||||||
|
# Dashboard
|
||||||
|
|
||||||
|
Открывать на http://192.168.77.1:8080
|
||||||
|
|
||||||
|
# API
|
||||||
|
|
||||||
|
GET /api/health
|
||||||
|
|
||||||
|
GET /api/history
|
||||||
|
|
||||||
|
# Wi-Fi режим Raspberry Pi
|
||||||
|
|
||||||
|
SSID: fix_me
|
||||||
|
IP: 192.168.77.1
|
||||||
|
|
||||||
|
Подключение: ssh user@192.168.77.1
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|||||||
177
scripts/imi_wire.py
Executable file
177
scripts/imi_wire.py
Executable file
@@ -0,0 +1,177 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Имитатор параллельной шины для gpio-interrupt (Raspberry Pi 4)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import gpiod
|
||||||
|
import time
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import signal
|
||||||
|
from gpiod.line import Direction, Value
|
||||||
|
|
||||||
|
|
||||||
|
class ParallelBusImitator:
|
||||||
|
"""Имитатор 7-битной параллельной шины данных"""
|
||||||
|
|
||||||
|
# Временные константы (секунды)
|
||||||
|
DATA_SETUP_TIME = 0.000005
|
||||||
|
STROBE_WIDTH = 0.000005
|
||||||
|
HOLD_TIME = 0.000005
|
||||||
|
|
||||||
|
WR_PIN = 12 # BCM12
|
||||||
|
|
||||||
|
DATA_PINS_MAPPING = [
|
||||||
|
(21, 18, "D0 (LSB)"),
|
||||||
|
(7, 27, "D1"),
|
||||||
|
(6, 22, "D2"),
|
||||||
|
(5, 23, "D3"),
|
||||||
|
(25, 17, "D4"),
|
||||||
|
(24, 20, "D5"),
|
||||||
|
(23, 21, "D6"),
|
||||||
|
(22, None, "D7 (не подключён)"),
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, use_mask=True, chip_path='/dev/gpiochip0'):
|
||||||
|
self.use_mask = use_mask
|
||||||
|
self.counter = 0
|
||||||
|
self.active_pins = []
|
||||||
|
|
||||||
|
for wpi, bcm, desc in self.DATA_PINS_MAPPING:
|
||||||
|
if bcm is not None:
|
||||||
|
self.active_pins.append((wpi, bcm, desc))
|
||||||
|
|
||||||
|
self.data_pins_bcm = [bcm for _, bcm, _ in self.active_pins]
|
||||||
|
self.all_pins = self.data_pins_bcm + [self.WR_PIN]
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.chip = gpiod.Chip(chip_path)
|
||||||
|
self.lines = self.chip.request_lines(
|
||||||
|
{
|
||||||
|
p: gpiod.LineSettings(
|
||||||
|
direction=Direction.OUTPUT,
|
||||||
|
output_value=Value.INACTIVE
|
||||||
|
)
|
||||||
|
for p in self.all_pins
|
||||||
|
},
|
||||||
|
consumer="par-bus-imitator"
|
||||||
|
)
|
||||||
|
except PermissionError:
|
||||||
|
print("Нет прав доступа к GPIO. Используйте sudo.")
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка GPIO: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
bit_width = 4 if use_mask else len(self.data_pins_bcm)
|
||||||
|
|
||||||
|
print(f"✓ Имитатор шины ({bit_width}-бит режим)")
|
||||||
|
print("Нажмите Ctrl+C для остановки\n")
|
||||||
|
|
||||||
|
def send_byte(self, value):
|
||||||
|
"""Отправка одного байта"""
|
||||||
|
if self.use_mask:
|
||||||
|
value &= 0x0F
|
||||||
|
else:
|
||||||
|
value &= 0x7F
|
||||||
|
|
||||||
|
out = {}
|
||||||
|
|
||||||
|
for bit_pos, bcm_pin in enumerate(self.data_pins_bcm):
|
||||||
|
bit = (value >> bit_pos) & 1
|
||||||
|
out[bcm_pin] = Value.ACTIVE if bit else Value.INACTIVE
|
||||||
|
|
||||||
|
self.lines.set_values(out)
|
||||||
|
time.sleep(self.DATA_SETUP_TIME)
|
||||||
|
|
||||||
|
self.lines.set_value(self.WR_PIN, Value.ACTIVE)
|
||||||
|
time.sleep(self.STROBE_WIDTH)
|
||||||
|
self.lines.set_value(self.WR_PIN, Value.INACTIVE)
|
||||||
|
time.sleep(self.HOLD_TIME)
|
||||||
|
|
||||||
|
self.counter += 1
|
||||||
|
self.show_status(value)
|
||||||
|
|
||||||
|
def show_status(self, value):
|
||||||
|
"""Красивый статус без спама"""
|
||||||
|
bits = 4 if self.use_mask else 7
|
||||||
|
bin_val = format(value, f'0{bits}b')
|
||||||
|
|
||||||
|
msg = (
|
||||||
|
f"\r▶ Передача: "
|
||||||
|
f"HEX=0x{value:02X} | "
|
||||||
|
f"BIN={bin_val} | "
|
||||||
|
f"Пакетов: {self.counter}"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(msg, end='', flush=True)
|
||||||
|
|
||||||
|
def send_sequence(self, values, delay=0.2):
|
||||||
|
for val in values:
|
||||||
|
self.send_byte(val)
|
||||||
|
time.sleep(delay)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
if hasattr(self, 'lines'):
|
||||||
|
self.lines.release()
|
||||||
|
if hasattr(self, 'chip'):
|
||||||
|
self.chip.close()
|
||||||
|
|
||||||
|
|
||||||
|
def handle_signal(signum, frame):
|
||||||
|
print("\nЗавершение работы...")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
signal.signal(signal.SIGINT, handle_signal)
|
||||||
|
signal.signal(signal.SIGTERM, handle_signal)
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Имитатор параллельной шины'
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument('--no-mask', action='store_true',
|
||||||
|
help='7-битный режим')
|
||||||
|
parser.add_argument('--test',
|
||||||
|
choices=['default', 'counter', 'pattern'],
|
||||||
|
default='default')
|
||||||
|
parser.add_argument('--delay', type=float, default=0.2)
|
||||||
|
parser.add_argument('--single', type=str)
|
||||||
|
parser.add_argument('--chip', default='/dev/gpiochip0')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
imitator = ParallelBusImitator(
|
||||||
|
use_mask=not args.no_mask,
|
||||||
|
chip_path=args.chip
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if args.single:
|
||||||
|
val = int(args.single, 16)
|
||||||
|
imitator.send_byte(val)
|
||||||
|
print()
|
||||||
|
return
|
||||||
|
|
||||||
|
if args.test == 'default':
|
||||||
|
test_data = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40]
|
||||||
|
elif args.test == 'counter':
|
||||||
|
test_data = list(range(128))
|
||||||
|
else:
|
||||||
|
test_data = [0x00, 0x01, 0x03, 0x07, 0x0F, 0x55, 0x2A]
|
||||||
|
|
||||||
|
while True:
|
||||||
|
imitator.send_sequence(test_data, args.delay)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nОстановка...")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\nОшибка: {e}")
|
||||||
|
finally:
|
||||||
|
imitator.cleanup()
|
||||||
|
print("✓ GPIO освобождены")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
16
scripts/monitor-gpio.service
Normal file
16
scripts/monitor-gpio.service
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=GPIO Interrupt Capture Service
|
||||||
|
Requires=monitor-gpio.socket
|
||||||
|
After=monitor-gpio.socket
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
WorkingDirectory=/home/user/WiringPi/examples
|
||||||
|
|
||||||
|
ExecStart=/home/user/WiringPi/examples/gpio-interrupt -
|
||||||
|
StandardOutput=socket
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
Restart=always
|
||||||
|
RestartSec=2
|
||||||
12
scripts/monitor-gpio.socket
Normal file
12
scripts/monitor-gpio.socket
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=GPIO FIFO Pipe
|
||||||
|
|
||||||
|
[Socket]
|
||||||
|
ListenFIFO=/tmp/gpio_pipe
|
||||||
|
SocketMode=0666
|
||||||
|
SocketUser=user
|
||||||
|
SocketGroup=user
|
||||||
|
RemoveOnStop=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=sockets.target
|
||||||
Reference in New Issue
Block a user