From 89b089df935aa6d8fb1af444e907aeed149bda4e Mon Sep 17 00:00:00 2001 From: thek4n Date: Fri, 12 Jun 2026 00:49:58 +0300 Subject: [PATCH] Initial commit --- justfile | 9 + logic_analyzer/logic_analyzer.ino | 733 ++++++++++++++++++++++++++++++ 2 files changed, 742 insertions(+) create mode 100644 justfile create mode 100644 logic_analyzer/logic_analyzer.ino diff --git a/justfile b/justfile new file mode 100644 index 0000000..1d6a95e --- /dev/null +++ b/justfile @@ -0,0 +1,9 @@ +alias compile := build + +build: + arduino-cli compile --fqbn esp8266:esp8266:nodemcuv2 + + +alias flash := upload +upload: + arduino-cli upload --fqbn esp8266:esp8266:nodemcuv2 --port /dev/ttyUSB0 diff --git a/logic_analyzer/logic_analyzer.ino b/logic_analyzer/logic_analyzer.ino new file mode 100644 index 0000000..b720bba --- /dev/null +++ b/logic_analyzer/logic_analyzer.ino @@ -0,0 +1,733 @@ +#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 + + + +
+
+

🔍 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); +}