Прошу помощи в декодирование протокола

Доброго времени суток.
Прошу помощи в декодировании протокола китайского парктроника.
Устройство будет использоваться не по прямому назначению и стоит задача, изготовить свой дисплей вместо штатного, чтобы информация выводилась с учетом новых реалий.
Честно говоря, я думал, что данные от блока к дисплею будут идти или по UART или как это часто бывает, обычный шим, где шириной импульса определяется 0 или 1.
Но не тут то было, и если верить анализатору, то в данном устройстве используется манчестерское кодирование. (но это не точно ) :slightly_smiling_face:
Провёл, уже очень много времени в раздумьях глядя на информацию с анализатора и даже нашел в ней биты которые мне необходимы, но понял, что без посторонней помощи я не осилю задачу получить всю эту красоту в бинарном виде в ESP32.
Решил попробовать попросить помощи тут.

Немного информации в картинках


Вот так выглядят пакеты. Пакеты идут через интервал 40 ms ( иногда почему-то интервал меняется в большую сторону)


Каждый пакет состоит как бы из двух частей. Которые всегда одинаковые между собой. Видимо это сделано чтобы потом можно было сравнить части и таким образом исключить ошибки в приёме. ( или я ошибаюсь?)


Тут уже видны 0 и 1 данных.


Ну и тайминги на всякий случай )

Вопрос, как это всё принять в бинарном виде? С чего начать, каким путем пойти, может у кого-то есть наработки по такому кодированию.
Очень рассчитываю на вашу помощь.

вопрос в чем? как принять и преобразовать в последовательность бит? для арду есть функция pulsein, например. Ориентируясь на длину можно целиком этот пакет и его повтор прочесть. Если сам протокол разобрать надо, то самой нужной инфы не хватает: как меняется пакет при изменении показаний датчика?

Прием бит я бы сделал примерно так:

  1. объявляем “макс” = максимально возможное кол-во изменения состояния входа датчика при приеме 1 пакета
  2. объявляем “массив[макс]” для хранения моментов времени в микросекундах
  3. объявляем “индекс = 0”
  4. вешаем прерывание на изменение (в обе стороны) состояния пина датчика
  5. в прерывании пишем время его сработки (микросекунд) в “массив[индекс]”, делаем “индекс++” (возможно, следует проверить индекс < макс, если нет - сбросить индекс)
  6. в loop(), если “индекс > 0” (или другой более реалистичный минимум) сравниваем “массив[индекс-1]” и текущее время. Если разница больше некого порога, по которому стоит судить, что пакет закончился - читаем данные из массива, иначе ничего не делаем
  7. Помним, что пакет начинается с high to low, и это первая “единица”, переданная в пакете. Также помним, что каждый новый элемент свидетельствует о смене состояния выхода, и они чередуются. То есть массив[0] порт упал, массив[1] - поднялся и так далее. Четные это падения, нечетные это поднятия. Кстати косвенный признак корректности: у вас пакет заканчивается поднятием вывода, то есть запишется нечетное кол-во элементов, но индекс будет на единицу больше, т.е. “индекс % 2 == 0”
  8. В цикле от “н = 0 до (ВАЖНО) индекс-2” считаем разницу во времени возникновения событий “дельта = массив[н + 1] - массив[н]”. У вас символ “0” или “1” будет “н % 2” (четность индекса), а количество подряд идущих символов = “дельта / длительность одного импульса”
  9. после п. 8 сбрасываем индекс = 0
1 лайк

А на нем ченть написано? Модель там, еще что то.

Я бы начал с изучения интернета на тему протоколов. Наверняка уже кто-то разбирался с этой темой.

Как принять: ну, при таких таймингах можно тупо в цикле читать состояние пина. Но для начала лучше разобраться с протоколом, может там что-то стандартное и тогда можно будет выбрать способ приема оптимальный.
ЗЫЖ
Вот тут - Hacking "Chinese" parking sensors - General Guidance - Arduino Forum товарищ использовал UART для приема данных, и, что нехарактерно, что-то там принял.

Если тайминги - сотни миллисекунд, то можно просто читать состояние пина в цикле:

digitalRead(pin);

Можно использовать прерывания, тогда твоя функция будет вызываться при изменении с 0 на 1 и с 1 на 0. В этой функции ты можешь считывать millis() и таким образом ты получишт и значения (1 или 0) и расстояния в миллисекундах между сменами с одного уровня на другой.

так:

pinAttachInterrupt( ... ) ; 

или эдак:

  gpio_set_intr_type(pin, GPIO_INTR_ANYEDGE);
  gpio_install_isr_service(...);            
  gpio_isr_handler_add( ... );

Сам себя процитирую, потому что редактировать посты мне не дают.
Первый бит игнорируется, потом идут три байта данных.

Там сотни микросекунд вроде. В первом посте есть скрин.

Так, ставим прерывание по CHANGE
В функции засекаем время между сменой одного уровня на другой.
Как понять где 0 а где 1 ? Тут же не шириной отличается 0 от 1.

Беглый поиск показал, что кода для декодирования manchester code - пруд пруди. есть библиотеки для ардуины даже, но я их не смотрел.

да, временные отметки вам даже и не понадобятся.

Ну я бы не сказал, что прям пруд пруди, но всё что я нашел и попробовал, успеха не принесло. Видимо из-за отсутствия настроек и моих кривых рук (

я не сталкивался никогда с этим кодированием, но пробежался по вики и понял, что там, не смотря на то, что CLK никакого нет, на самом деле он есть :). И используется при кодировании. Тут ноль от 1 оличается фазой.

Кодируется так:

Допустим у нас есть CLOCK, 10кГц.
Мы передаем биты данных 1 и 0.
Когда мы хотим передать 1 мы передаем CLOCK
Когда мы хотим передать 0 мы передаем инвертированный CLOCK.

В IEEE стандарте выъодное значение еще и инвертируется, и это как раз на твоей картинке видно: Биты 00001111, а значение 240. Это как раз инверсия .

Так как никакого опорного CLK у тебя нет, то его как бы восстанавливают из данных, т.е. используют 1 или несколько бит данных для отметки начала тактирования. На твоей картинке это как раз первый бит, который игнорируется.

На твоем месте я бы поискал готовый код на GitHub для Manchester decode. Он скорее всего простенький и перенести его на ESP32 не составит труда.

Самому изобретать велосипед , в принципе, тоже можно, скилы прокачать.

Да, да, я как раз ниже написал, что когда скрины делал, забыл инверсию включить.

Дело в том, что я сюда уже пришел с последней надеждой. (
До этого, я уже перерыл всё что смог найти и попробовал то, на что хватило знаний.
Стыдно сказать, но я уже месяц ежедневно с этим всем бьюсь и продвинулся только в том, что нашел по картинкам с анализатора, чем это кодировано и в каких битах находятся нужные данные.
Буду рыть дальше. Вам огромное спасибо за участие.

я ж вам написал в первом же сообщении, как это сделать…

примерный код, мне не на чем тестить, попробуйте:

// максимально возможное кол-во смен состояния входа за прием одного пакета (включая следом идущую копию)
const uint16_t maxc = 255; // представить себе, что там будет 1010101010101010101010... короче количество бит + 1
int16_t ind = -1;
uint16_t microsArr[maxc];
const uint16_t bitc = 200; // количество микросекунд, соотв. длительности импульса одного бита в пакете
const uint16_t cycle_half = 20000 / 2; // половина цикла (периода) отправки пакетов

void ISR (void* para) { // повесить на GPIO_INTR_ANYEDGE

  ind++;
  if (ind == maxc) ind = 0; 
  microsArr[ind] = micros();

}

loop() {
  
  if (ind > 0) if (micros() - microsArr[ind] > cycle_half) {
  
    // в массиве шото есть, и последнее шото добавилось достаточно давно
  
    for (uint16_t i = 0; i < ind; i++) {
    
      uint16_t dt = microsArr[ind + 1] - microsArr[ind]; // сколько времени между соседними сменами состояний
      uint16_t cnt = dt / bitc + (dt % bitc > bitc / 2); // сколько бит вмещается в это время, (dt % bitc > bitc / 2) - это для гашения погрешности замеров
      bool sym = i % 2
      
      for (uint16_t j = 0; j < cnt; j++) Serail.print(sym, BIN); // выводим символ(ы)
      
    }
    
    Serail.println(); // завершаем строку
    ind = -1; // сбрасываем индекс
  
  }

}

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

это вот точно не тут должно быть

не, не обязательно

оно и не скетч было собсно, а код )))

мне просто тож проверять особо не на чем, попробуйте так:

#define BTNPIN 2

// максимально возможное кол-во смен состояния входа за прием одного пакета (включая следом идущую копию)
const uint16_t maxc = 255;  // представить себе, что там будет 1010101010101010101010... короче количество бит + 1
int16_t ind = -1;
uint16_t microsArr[maxc];
const uint16_t bitc = 200;              // количество микросекунд, соотв. длительности импульса одного бита в пакете
const uint16_t cycle_half = 20000 / 2;  // половина цикла (периода) отправки пакетов

//void ISR (void* para) { // повесить на GPIO_INTR_ANYEDGE
void chng() {

  ind++;
  if (ind == maxc) ind = 0;
  microsArr[ind] = micros();
}

void setup() {
  pinMode(BTNPIN, INPUT_PULLUP);
  Serial.begin(115200);

  attachInterrupt(0, chng, CHANGE);  
}


void loop() {

  if (ind > 0) if (micros() - microsArr[ind] > cycle_half) {

      // в массиве шото есть, и последнее шото добавилось достаточно давно

      for (uint16_t i = 0; i < ind; i++) {

        uint16_t dt = microsArr[ind + 1] - microsArr[ind];  // сколько времени между соседними сменами состояний
        
        uint16_t cnt = dt / bitc;  // сколько бит вмещается в это время
        if (dt % bitc > bitc / 2) cnt++; // если dt получилось чуть меньше числа, кратного bitc 
        bool sym = i % 2; // какой символ приняли

        for (uint16_t j = 0; j < cnt; j++) Serial.print(sym ? "1" : "0");  // выводим символ(ы)


      }
      
      Serial.println();  // завершаем строку
      ind = -1;          // сбрасываем индекс
      
    }
    
   // 
    
}

Что, и вот эта библиотека не работает?

Одной из первых её пробовал.
Точно не помню уже, какой был результат, но не тот что нужно.
Возможно, что-то подстроить надо было, но знаний не хватило (

Там же примеры есть.

Если и это видел, я больше не буду тему засорять:

1 лайк

ощущается как будто-бы более осмысленно. Только uint16_t cnt = dt / bitc; // сколько бит вмещается в это время походу неправильно количество считает. А длина 1 бит в сигнале 200 микросекунд? и период повторения 20000 микросекунд? т.е. 20 миллисекунд

Посмотри библиотеку из #17

Да, 200 насколько я понимаю )


Я правильно измерил? 10мс в одном куске )