Не распознаётся массив в websoket

нет неправильно. команда serializeJson(doc, output); делает - serializeJson() serializes a JsonDocument to create a minified JSON document, i.e. a document without spaces or line break between values.

то есть оптимизирует и удаляет лишнее чтоб было меньше отправлять а отправляешь все равно через ws.textAll

Смотри, если у тебя небольшой “одноуровневый“ JSON то можно его руками собирать и отправлять без библиотеки. Самое главное не отправлять так

for (int n = 0; n <= 7; n++) {
      if (switchVar1 & (1 << n)) {  // array[0-7]
        ws.textAll(String(1));
      }

или так

ws.textAll(String("TEMP:") + tempC);
ws.textAll(String("HUMID:") + byte(humidity));
ws.textAll(String("SENSOR:") + byte(sensor));

Сначала собираешь данные отправки в одну строку, не важно как, с библиотекой или без, и потом одним пакетом отправляешь.
Может придут щас умные люди и научат нас отправлять данные по ws пачками, главное чтоб не в теории, а с рабочим кодом, но я так не умею!

Вот смотри, рабочие коды, проверено работает, от твоего отличается только тем что данные генерятся рандомно а не берутся с сенсоров.

index.h

const char htmlPage[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PIN Display</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 0;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        }
        .container {
            background: white;
            padding: 40px;
            border-radius: 20px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            text-align: center;
        }
        h1 {
            margin: 0 0 30px 0;
            color: #333;
        }
        .pin-display {
            display: flex;
            gap: 10px;
            justify-content: center;
            margin-bottom: 20px;
        }
        .pin-digit {
            width: 50px;
            height: 60px;
            background: #f0f0f0;
            border: 2px solid #667eea;
            border-radius: 10px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 32px;
            font-weight: bold;
            color: #333;
            transition: all 0.3s ease;
        }
        .pin-digit.active {
            background: #667eea;
            color: white;
            transform: scale(1.1);
        }
        .status {
            margin-top: 20px;
            padding: 10px;
            border-radius: 8px;
            font-size: 14px;
        }
        .status.connected {
            background: #d4edda;
            color: #155724;
        }
        .status.disconnected {
            background: #f8d7da;
            color: #721c24;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>PIN Мониторинг</h1>
        <div class="pin-display" id="pinDisplay"></div>
        <div class="status disconnected" id="status">Отключено</div>
    </div>

    <script>
        // адрес WebSocket сервера
        const WS_URL = 'ws://' + window.location.host + '/ws';
        
        let ws;
        const pinDisplay = document.getElementById('pinDisplay');
        const statusEl = document.getElementById('status');

        // динамически нарисуем  8 элементов для отображения PIN
        for (let i = 0; i < 8; i++) {
            const digit = document.createElement('div');
            digit.className = 'pin-digit';
            digit.textContent = '0';
            digit.id = `pin-${i}`;
            pinDisplay.appendChild(digit);
        }

        function connectWebSocket() {
            ws = new WebSocket(WS_URL);

            ws.onopen = () => {
                console.log('WebSocket подключен');
                statusEl.textContent = 'Подключено';
                statusEl.className = 'status connected';
            };

            ws.onmessage = (event) => {
                try {
                    const data = JSON.parse(event.data);
                    
                    if (data.pin && Array.isArray(data.pin)) {
                        updatePinDisplay(data.pin);
                    }
                } catch (error) {
                    console.error('Ошибка парсинга JSON:', error);
                }
            };

            ws.onerror = (error) => {
                console.error('WebSocket ошибка:', error);
                statusEl.textContent = 'Ошибка подключения';
                statusEl.className = 'status disconnected';
            };

            ws.onclose = () => {
                console.log('WebSocket отключен');
                statusEl.textContent = 'Отключено';
                statusEl.className = 'status disconnected';
                
                // Переподключение через 3 секунды
                setTimeout(connectWebSocket, 3000);
            };
        }

        function updatePinDisplay(pinArray) {
            pinArray.forEach((value, index) => {
                const digitEl = document.getElementById(`pin-${index}`);
                if (digitEl) {
                    digitEl.textContent = value;
                    
                    // подсветим активные (если значение 1)
                    if (value === 1) {
                        digitEl.classList.add('active');
                    } else {
                        digitEl.classList.remove('active');
                    }
                }
            });
        }

        // Запускаем подключение
        connectWebSocket();
    </script>
</body>
</html>
)rawliteral";

ino

//#include <ArduinoJson.h>
//#include <ArduinoJson.hpp>

#ifdef ARDUINO_ARCH_ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#else
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>
#include "index.h"

const char *ssid = "***";
const char *password = "***";

#ifdef ARDUINO_ARCH_ESP8266
const int LED_PIN = LED_BUILTIN;
#define LED_ON LOW
#define LED_OFF HIGH
byte const SENSOR_PIN = A0;
#else
const int LED_PIN = 2;
#define LED_ON HIGH
#define LED_OFF LOW
byte const SENSOR_PIN = 34;
#endif

//#include "DHT.h"  //https://github.com/adafruit/Adafruit_Sensor
#define DHTPIN D1
#define DHTTYPE DHT11
//DHT dht(DHTPIN, DHTTYPE);

bool ledState = false;
//byte const pinLED = LED_BUILTIN;

int sensor = 0;
float humidity = 0;
float tempC = 0;
float tempF = 0;

AsyncWebServer server(80);
AsyncWebSocket ws("/ws");

//#define LATCHPIN D2     //GREEN 9 chip CD4021
//#define DATAPIN D3      //YELLOW 3 chip CD4021
//#define CLOCKPIN D4     // GREY 10 chip CD4021
byte switchVar1 = 72;   //01001000 регистр сдвига CD4021
byte switchVar2 = 159;  //10011111

void setup() {
  Serial.begin(115200);
  while (!Serial) { ; }
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LED_OFF);
  //pinMode(LATCHPIN, OUTPUT);
  //pinMode(CLOCKPIN, OUTPUT);
  //pinMode(DATAPIN, INPUT);
  //pinMode(D2, OUTPUT);
  //digitalWrite(D2, LOW);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print('.');
  }
  timeoutNet();
  // attach AsyncWebSocket
  ws.onEvent(onWsEvent);
  server.addHandler(&ws);
  // respond to GET requests on URL /heap
  server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *r) {
    r->send_P(200, "text/html", htmlPage);
  });

  server.on("/health", HTTP_GET, [](AsyncWebServerRequest *r) {
    r->send(200, "text/plain", "ok");
  });
  server.begin();
}

void loop() {
  ws.cleanupClients();
  //JsonDocument doc;
  static unsigned long last = 0;
  if (millis() - last > 3000) {
    last = millis();
    String lWSData = "{\"pin\":[";

    for (int n = 0; n <= 7; n++) {
      lWSData = lWSData + random(0,2);
      if (n<7){lWSData = lWSData + ",";}
      /*
      if (switchVar1 & (1 << n)) {  // array[0-7]
        pin.add(1);
        ws.textAll(String(1));
        Serial.print("1");
        Serial.print("|");  //nclose
      } else {
        pin.add(0);
        ws.textAll(String(0));
        Serial.print("0");
        Serial.print("|");  //nopen
      }
      */
    }
    lWSData = lWSData+"]}";
    Serial.print(lWSData);
    ws.textAll(lWSData);
    // cheek register СВ4021, contact with rezistors 1,5kOm
    //digitalWrite(LATCHPIN, 1);
    //delayMicroseconds(20);
    //digitalWrite(LATCHPIN, 0);
    //--- 1 register СВ4021
    //switchVar1 = shiftIn(DATAPIN, CLOCKPIN);

    //здесть ненадо дергать сокет, надо собрать 1 пакет данных и отправить 
    // на стороне клиена расшифровать и расставить по ячейкам.
    /*
    switchVar1 = 125;
    for (int n = 0; n <= 7; n++) {
      if (switchVar1 & (1 << n)) {       // array[0-7]
        ws.textAll(String("P=") + "1");  //nclose
      } else {
        ws.textAll(String("P=") + "0");  //nopen
      }
    }
    //--- 2 register СВ4021
    //switchVar2 = shiftIn(DATAPIN, CLOCKPIN);
    switchVar2 = 252;
    for (int n = 0; n <= 7; n++) {
      if (switchVar2 & (1 << n)) {       // array[7-15]
        ws.textAll(String("P=") + "1");  //nclose
      } else {
        ws.textAll(String("P=") + "0");  //nopen
      }
    }
*/
    updateSensors();
    //ws.textAll(String("TEMP:") + tempC);
    //ws.textAll(String("HUMID:") + byte(humidity));
    //ws.textAll(String("SENSOR:") + byte(sensor));
    // Write JSON document
    //serializeJsonPretty(doc, client);

  }
}
void updateSensors() {
  sensor = map(analogRead(SENSOR_PIN), 0, 1023, 0, 100);
  humidity = random(0,100);
  tempC = random(-273,100);      // Read temperature as Celsius
  tempF = random(459,212);  // Read temperature as Fahrenheit (isFahrenheit = true)

  /*humidity = dht.readHumidity();
  tempC = dht.readTemperature();      // Read temperature as Celsius
  tempF = dht.readTemperature(true);  // Read temperature as Fahrenheit (isFahrenheit = true)
  */
  //if any value is isnan (not a number) then there is an error
  if (isnan(humidity) || isnan(tempC) || isnan(tempF)) {
    Serial.println("Error reading from the DHT11.");
  } else {
    String data = "";
    data = String(data + sensor);
    data = String(data + "|");
    data = String(data + byte(humidity));
    data = String(data + "|");
    data = String(data + tempC);
    data = String(data + "|");
    data = String(data + tempF);
    Serial.println(data);  // display the data in the serial monitor
  }
}
void onWsEvent(AsyncWebSocket *srv, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
  if (type == WS_EVT_CONNECT) {
    client->text(ledState ? "ON" : "OFF");
    return;
  }
  if (type == WS_EVT_DATA) {
    AwsFrameInfo *info = (AwsFrameInfo *)arg;
    if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
      String msg;
      msg.reserve(len);
      for (size_t i = 0; i < len; i++) msg += (char)data[i];
      if (msg == "TOGGLE") {
        ledState = !ledState;
        digitalWrite(LED_PIN, ledState ? LED_ON : LED_OFF);
        //ws.textAll(ledState ? "ON" : "OFF");
      }
    }
  }
}
void timeoutNet() {  // connection with timeout
  int count = 0;
  //digitalWrite(LED_BUILTIN, HIGH);
  while ((WiFi.status() != WL_CONNECTED) && count < 17) {
    Serial.print(".");
    count++;
    delay(500);
  }
  //digitalWrite(LED_BUILTIN, LOW);
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("");
    Serial.print("Failed to connect to ");
    while (1)
      ;
  }
  Serial.println(ssid);
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());
}
// 2 регистра сдвига
byte shiftIn(int DATA, int CLK) {
  int i;
  int temp = 0;
  byte pinState;
  byte inDATE = 240;

  /*
  pinMode(CLK, OUTPUT);
  pinMode(DATA, INPUT);
  for (i = D2; i >= 0; i--) {
    digitalWrite(CLK, 0);
    delayMicroseconds(2);
    temp = digitalRead(DATA);
    if (temp) {
      pinState = 1;
      // несмотря ни на что, задаем биту значение «0»:
      inDATE = inDATE | (1 << i);
    } else {
      pinState = 0;
    }
    digitalWrite(CLK, 1);
  }
  */
  return inDATE;
}

результат

в коде много что заккоментировал, думаю разберешься.

и как сказал пиши все в одну JSON строку, можно без библиотеки типа так {“pin”:[0,1,1,0,0,1,0,0], “tem“:25, “hum“: 81} а на стороне клиента разбирай.
если у тебя конечно там сложное дерево то тогда проще библиотекой собирать.

3 лайка

собрал так:

void loop() {
  ws.cleanupClients();

  static unsigned long last = 0;
  if (millis() - last > 3000) {
    last = millis();
    // Create the "digital" array
    digitalWrite(LATCHPIN, 1);
    delayMicroseconds(20);
    digitalWrite(LATCHPIN, 0);
    switchVar1 = SHIFT(DATAPIN, CLOCKPIN);//1 register СВ4021
    String lWSData = "{\"pin\":[";
    for (int n = 0; n <= 7; n++) {
      if (switchVar1 & (1 << n)) {  // array[0-7]
        lWSData = lWSData + 1;
      } else {
        lWSData = lWSData + 0;
      }
      if (n < 7) { lWSData = lWSData + ","; }
    }
    lWSData = lWSData + "]}";
    Serial.print(lWSData);
    ws.textAll(lWSData);
    Serial.println();
  }
}

в консоле ws

{“pin”:[0,0,0,0,0,0,0,0]} при замыкании пинов 1 отображаются. :slight_smile:

позже преобразую 0 в виде кружков зеленые статические, а 1 в красные плавно мерцающие , чтоб страница не бала статичной. т.н. индикация состояний насосов и обрудования СТО.

1 лайк

для 2х регистров добавил:

void loop() {
  ws.cleanupClients();

  static unsigned long last = 0;
  if (millis() - last > 3000) {
    last = millis();
    // Create the "digital" array
    digitalWrite(LATCHPIN, 1);
    delayMicroseconds(20);
    digitalWrite(LATCHPIN, 0);
    switchVar1 = SHIFT(DATAPIN, CLOCKPIN);  //1 register СВ4021
    switchVar2 = SHIFT(DATAPIN, CLOCKPIN);  //2 register СВ4021
    String lWSData = "{\"pin\":[";
    for (int n = 0; n <= 7; n++) {
      if (switchVar1 & (1 << n)) {  // array[0-7]
        lWSData = lWSData + 1;
        lWSData = lWSData + ",";
      } else {
        lWSData = lWSData + 0;
        lWSData = lWSData + ",";
      }
    }
    for (int n = 0; n <= 7; n++) {
      if (switchVar2 & (1 << n)) {  // array[8-16]
        lWSData = lWSData + 1;
        lWSData = lWSData + ",";
      } else {
        lWSData = lWSData + 0;
        lWSData = lWSData + ",";
      }
    }
    lWSData = lWSData + "]}";
    Serial.print(lWSData);
    ws.textAll(lWSData);
    Serial.println();
  }
}

консоль

{"pin":[0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0]}

т.о. можно опрашивать кучу DI используя 3 пина на ESP + N дешёвых регистра СВ4021.

понимаю, что компилятор как и меня корежит, но он за вас додумывает…
глаз не режет?

дело в том что после обработки первого цикла между символами 8и 9 цифры нету зпт, возникает ошибка парсинга

дело в том, что вы пытаетесь сделать операцию конкатенации между строкой и числом!
в всемогущей ардуино для домохозяек, это прощается и срабатывает

void setup(void) {
  Serial.begin(9600);
  String s = "строка";
  s = s + 1;
  Serial.println(s);
}
void loop(void) {
}

“нормальный” компилятор выдаст, минимум предупреждение.
может я и не прав, но глаз мне это режет точно.

2 лайка

я представляю, что операции класса String можно перегрузить и до такой дичи

void setup(void) {
  Serial.begin(9600);
  String s = "1";
  s = s + 1;
  Serial.print("Result=");
  Serial.println(s);
}
void loop(void) {
}
Result=2

:man_facepalming:

1 лайк

ИЗМЕНЕНИЯ КОДА

void loop() {
  ws.cleanupClients();

  static unsigned long last = 0;
  if (millis() - last > READ_INTERVAL) {
    last = millis();
    // Create the "digital" array
    digitalWrite(LATCHPIN, 1);
    delayMicroseconds(20);
    digitalWrite(LATCHPIN, 0);
    switchVar1 = SHIFT(DATAPIN, CLOCKPIN);  //1 register СВ4021
    switchVar2 = SHIFT(DATAPIN, CLOCKPIN);  //2 register СВ4021
    String lWSData = "{\"pin\":[";
    for (int n = 0; n <= 7; n++) {
      if (switchVar1 & (1 << n)) {  // array[0-7]
        lWSData += 1;
      } else {
        lWSData += 0;
      }
      if (n <= 7) { lWSData += ","; } // ВНИМАНИЕ НА ЗНАКИ СРАВНЕНИЯ <=
    }
    for (int n = 0; n <= 7; n++) {
      if (switchVar2 & (1 << n)) {  // array[0-7]
        lWSData += 1;
      } else {
        lWSData += 0;
      }
      if (n < 7) { lWSData += ","; } // n<7 устранить последнюю зпт
    }
    lWSData = lWSData + "]}";
    Serial.print(lWSData);
    ws.textAll(lWSData);
    Serial.println();

нет, резулт будет “11”, что вполне логично