diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cebea1c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+
+firmware/logic_analyzer/root_html.h
diff --git a/assets/root.html b/assets/root.html
new file mode 100644
index 0000000..432de7d
--- /dev/null
+++ b/assets/root.html
@@ -0,0 +1,40 @@
+
+
+
+ ";
+
+ Logic Analyzer
+
+
+
+
+
+
+
+
+
+function connectWebSocket() {
+ ws = new WebSocket('ws://' + window.location.hostname + ':81');
+
+ ws.onopen = function() {
+ console.log('WebSocket connected');
+ updateStatus('Подключено');
+ };
+
+ ws.onmessage = function(event) {
+ const data = JSON.parse(event.data);
+ if (data.type === 'data') {
+ currentData = data.channels;
+ currentSamples = data.samples;
+ drawAllWaveforms();
+ } else if (data.type === 'status') {
+ updateStatus(data.message);
+ }
+ };
+
+ ws.onclose = function() {
+ console.log('WebSocket disconnected');
+ updateStatus('Отключено. Переподключение...');
+ setTimeout(connectWebSocket, 1000);
+ };
+ }
diff --git a/firmware/logic_analyzer/logic_analyzer.ino b/firmware/logic_analyzer/logic_analyzer.ino
new file mode 100644
index 0000000..471732b
--- /dev/null
+++ b/firmware/logic_analyzer/logic_analyzer.ino
@@ -0,0 +1,170 @@
+#include
+#include
+#include
+#include
+
+#include "root_html.h"
+
+#define CH1_PIN D0
+#define CH2_PIN D1
+#define CH3_PIN D2
+#define CH4_PIN D5
+#define CH5_PIN D6
+#define CH6_PIN D7
+
+#define WIFI_AP_SSID "ESP8266_LogicAnalyzer"
+#define WIFI_AP_PASS "12345678"
+
+#define WEBSERVER_PORT 80
+#define WEBSOCKET_PORT 81
+
+
+const uint8_t PINS[] = {
+ CH1_PIN, CH2_PIN, CH3_PIN,
+ CH4_PIN, CH5_PIN, CH6_PIN,
+};
+
+volatile bool g_sample_active = false;
+
+ESP8266WebServer server(WEBSERVER_PORT);
+WebSocketsServer webSocket = WebSocketsServer(WEBSOCKET_PORT);
+
+
+typedef union {
+ unsigned char mask;
+ struct {
+ unsigned a0:1;
+ unsigned a1:1;
+ unsigned a2:1;
+ unsigned a3:1;
+ unsigned a4:1;
+ unsigned a5:1;
+ unsigned a6:1;
+ unsigned a7:1;
+ } byte;
+} Sample;
+
+#define SAMPLES_BUFFER_CAP 64
+Sample SAMPLES[SAMPLES_BUFFER_CAP] = {};
+volatile uint8_t g_samples_idx = 0;
+
+
+Sample takeSample() {
+ Sample data;
+ data.mask = 0;
+
+ int pins_len = sizeof(PINS) / sizeof(PINS[0]);
+
+ for (uint8_t i = 0; i < pins_len; ++i) {
+ data.mask |= (!(!digitalRead(PINS[i])) << i);
+ }
+
+ return data;
+}
+
+void SerialSendPins(Sample ps) {
+ Serial.print("0b");
+ Serial.println(ps.mask, BIN);
+}
+
+void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
+ switch(type) {
+ case WStype_DISCONNECTED:
+ stopSampling();
+ break;
+
+ case WStype_CONNECTED:
+ startSampling();
+ webSocket.sendTXT(num, "{\"type\":\"status\",\"message\":\"Connected\"}");
+ break;
+ }
+}
+
+void startSampling() {
+ g_sample_active = true;
+}
+
+void stopSampling() {
+ g_sample_active = false;
+}
+
+void setup() {
+ Serial.begin(115200);
+
+ int pins_len = sizeof(PINS) / sizeof(PINS[0]);
+
+ for (uint8_t i = 0; i < pins_len; ++i) {
+ pinMode(PINS[i], INPUT);
+ }
+
+ WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PASS);
+ Serial.print("IP: ");
+ Serial.println(WiFi.softAPIP());
+
+ setupWebServer();
+ webSocket.begin();
+ webSocket.onEvent(webSocketEvent);
+
+ server.begin();
+ Serial.println("Ready");
+}
+
+void setupWebServer() {
+ server.on("/", rootHandler);
+}
+
+void rootHandler() {
+ server.send(200, "text/html", assets_root_html, assets_root_html_len);
+}
+
+void appendSamples() {
+ if (!g_sample_active) return;
+
+ static unsigned long lastTime = 0;
+ unsigned long now = micros();
+ unsigned long interval_mks = 100;
+
+ if (now - lastTime < interval_mks) return;
+
+ lastTime = now;
+
+ if (g_samples_idx == SAMPLES_BUFFER_CAP - 1) return;
+
+ SAMPLES[g_samples_idx] = takeSample();
+ g_samples_idx++;
+}
+
+void websocketSendSamples() {
+ if (g_samples_idx != SAMPLES_BUFFER_CAP - 1) return;
+
+ static unsigned long lastTime = 0;
+ unsigned long now = millis();
+
+ unsigned long interval_ms = 100;
+
+ if (now - lastTime < interval_ms) return;
+
+ lastTime = now;
+
+ // конвертировать SAMPLES в json
+ String output = "{\"type\":\"data\",\"len\":" + String(SAMPLES_BUFFER_CAP) + ",\"data\":[";
+ for (int i = 0; i < SAMPLES_BUFFER_CAP; ++i) {
+ output += String(SAMPLES[i].mask);
+ if (i < SAMPLES_BUFFER_CAP - 1) output += ",";
+ }
+ output += "]}";
+
+ webSocket.broadcastTXT(output);
+
+ g_samples_idx = 0;
+}
+
+void loop() {
+ server.handleClient();
+ webSocket.loop();
+
+ appendSamples();
+ websocketSendSamples();
+
+ delay(1);
+}
diff --git a/justfile b/justfile
index 1d6a95e..263cc3b 100644
--- a/justfile
+++ b/justfile
@@ -1,9 +1,19 @@
-alias compile := build
+#!/usr/bin/env -S just --justfile
+alias compile := build
build:
+ #!/bin/sh
+ xxd -i assets/root.html > firmware/logic_analyzer/root_html.h
+ cd firmware/logic_analyzer
arduino-cli compile --fqbn esp8266:esp8266:nodemcuv2
alias flash := upload
+
+[working-directory: 'firmware/logic_analyzer']
upload:
arduino-cli upload --fqbn esp8266:esp8266:nodemcuv2 --port /dev/ttyUSB0
+
+
+monitor:
+ arduino-cli monitor --port /dev/ttyUSB0
diff --git a/logic_analyzer/logic_analyzer.ino b/logic_analyzer/logic_analyzer.ino
deleted file mode 100644
index b720bba..0000000
--- a/logic_analyzer/logic_analyzer.ino
+++ /dev/null
@@ -1,733 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-
-// Настройки WiFi
-const char* ssid = "ESP8266_LogicAnalyzer";
-const char* password = "12345678";
-
-// Настройки пинов (4 канала для ESP8266)
-#define CH1_PIN 5 // D1
-#define CH2_PIN 4 // D2
-#define CH3_PIN 14 // D5
-#define CH4_PIN 12 // D6
-
-ESP8266WebServer server(80);
-WebSocketsServer webSocket = WebSocketsServer(81);
-
-// Буфер для данных - увеличен для 5 секунд при 5 кГц (25000 семплов)
-#define MAX_BUFFER_SIZE 25000
-uint8_t* logicBuffer;
-volatile int bufferIndex = 0;
-volatile bool samplingActive = false;
-volatile unsigned long sampleRate = 5000; // Максимальная частота по умолчанию
-volatile unsigned long lastSampleTime = 0;
-
-// Флаги для отправки
-volatile bool dataReady = false;
-unsigned long lastSendTime = 0;
-
-void setup() {
- Serial.begin(115200);
-
- // Выделяем память под буфер
- logicBuffer = (uint8_t*)malloc(MAX_BUFFER_SIZE);
- if (!logicBuffer) {
- Serial.println("Ошибка выделения памяти!");
- return;
- }
-
- // Настройка пинов
- pinMode(CH1_PIN, INPUT_PULLUP);
- pinMode(CH2_PIN, INPUT_PULLUP);
- pinMode(CH3_PIN, INPUT_PULLUP);
- pinMode(CH4_PIN, INPUT_PULLUP);
-
- // Запуск WiFi точки доступа
- WiFi.softAP(ssid, password);
- Serial.print("Точка доступа создана. IP: ");
- Serial.println(WiFi.softAPIP());
-
- // Настройка веб-сервера
- setupWebServer();
-
- // Запуск WebSocket
- webSocket.begin();
- webSocket.onEvent(webSocketEvent);
-
- server.begin();
- Serial.println("Сервер запущен");
-}
-
-void setupWebServer() {
- server.on("/", []() {
- server.send(200, "text/html", getHTMLPage());
- });
-}
-
-String getHTMLPage() {
- // HTML страница вынесена в отдельную функцию
- String html = R"rawliteral(
-
-
-
-
-
- ESP8266 Logic Analyzer
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-)rawliteral";
- return html;
-}
-
-void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
- switch(type) {
- case WStype_DISCONNECTED:
- Serial.printf("[%u] Отключен\n", num);
- stopSampling();
- break;
-
- case WStype_CONNECTED:
- Serial.printf("[%u] Подключен\n", num);
- webSocket.sendTXT(num, "{\"type\":\"status\",\"message\":\"Готов к работе\"}");
- break;
-
- case WStype_TEXT:
- {
- StaticJsonDocument<256> doc;
- DeserializationError error = deserializeJson(doc, payload);
-
- if (!error) {
- const char* command = doc["command"];
-
- if (strcmp(command, "start") == 0) {
- unsigned long newRate = doc["rate"];
- if (newRate != sampleRate) {
- stopSampling();
- sampleRate = newRate;
- }
- startSampling();
- Serial.printf("Начат сбор данных, частота: %lu Гц\n", sampleRate);
-
- String statusMsg = "{\"type\":\"status\",\"message\":\"Сбор данных... " + String(sampleRate) + " Гц\"}";
- webSocket.sendTXT(num, statusMsg);
- }
- else if (strcmp(command, "stop") == 0) {
- stopSampling();
- Serial.println("Сбор данных остановлен");
- webSocket.sendTXT(num, "{\"type\":\"status\",\"message\":\"Остановлен\"}");
- }
- }
- }
- break;
- }
-}
-
-void startSampling() {
- if (samplingActive) return;
-
- samplingActive = true;
- bufferIndex = 0;
- lastSampleTime = micros();
- dataReady = false;
- Serial.println("Sampling started");
-}
-
-void stopSampling() {
- if (!samplingActive) return;
-
- samplingActive = false;
- if (bufferIndex > 0) {
- sendData();
- bufferIndex = 0;
- }
- Serial.println("Sampling stopped");
-}
-
-void IRAM_ATTR sampleData() {
- if (!samplingActive) return;
-
- if (bufferIndex >= MAX_BUFFER_SIZE) {
- dataReady = true;
- return;
- }
-
- uint8_t sample = 0;
- sample |= (!digitalRead(CH1_PIN) << 0);
- sample |= (!digitalRead(CH2_PIN) << 1);
- sample |= (!digitalRead(CH3_PIN) << 2);
- sample |= (!digitalRead(CH4_PIN) << 3);
-
- logicBuffer[bufferIndex++] = sample;
-}
-
-void sendData() {
- if (bufferIndex == 0) return;
-
- StaticJsonDocument doc;
- doc["type"] = "data";
- doc["samples"] = bufferIndex;
-
- JsonArray channels = doc.createNestedArray("channels");
-
- for (int ch = 0; ch < 4; ch++) {
- JsonArray channelData = channels.createNestedArray();
- for (int i = 0; i < bufferIndex; i++) {
- channelData.add((logicBuffer[i] >> ch) & 1);
- }
- }
-
- String output;
- serializeJson(doc, output);
- webSocket.broadcastTXT(output);
-
- bufferIndex = 0;
-}
-
-void loop() {
- server.handleClient();
- webSocket.loop();
-
- if (dataReady) {
- dataReady = false;
- sendData();
- }
-
- if (samplingActive) {
- unsigned long now = micros();
- unsigned long interval = 1000000 / sampleRate;
-
- if (now - lastSampleTime >= interval) {
- lastSampleTime = now;
- sampleData();
- }
- }
-
- // Небольшая задержка для стабильности
- delay(1);
-}