Доброго времени суток.
Прошу помощи в декодировании протокола китайского парктроника.
Устройство будет использоваться не по прямому назначению и стоит задача, изготовить свой дисплей вместо штатного, чтобы информация выводилась с учетом новых реалий.
Честно говоря, я думал, что данные от блока к дисплею будут идти или по UART или как это часто бывает, обычный шим, где шириной импульса определяется 0 или 1.
Но не тут то было, и если верить анализатору, то в данном устройстве используется манчестерское кодирование. (но это не точно )
Провёл, уже очень много времени в раздумьях глядя на информацию с анализатора и даже нашел в ней биты которые мне необходимы, но понял, что без посторонней помощи я не осилю задачу получить всю эту красоту в бинарном виде в ESP32.
Решил попробовать попросить помощи тут.
Каждый пакет состоит как бы из двух частей. Которые всегда одинаковые между собой. Видимо это сделано чтобы потом можно было сравнить части и таким образом исключить ошибки в приёме. ( или я ошибаюсь?)
Вопрос, как это всё принять в бинарном виде? С чего начать, каким путем пойти, может у кого-то есть наработки по такому кодированию.
Очень рассчитываю на вашу помощь.
вопрос в чем? как принять и преобразовать в последовательность бит? для арду есть функция pulsein, например. Ориентируясь на длину можно целиком этот пакет и его повтор прочесть. Если сам протокол разобрать надо, то самой нужной инфы не хватает: как меняется пакет при изменении показаний датчика?
Прием бит я бы сделал примерно так:
объявляем “макс” = максимально возможное кол-во изменения состояния входа датчика при приеме 1 пакета
объявляем “массив[макс]” для хранения моментов времени в микросекундах
объявляем “индекс = 0”
вешаем прерывание на изменение (в обе стороны) состояния пина датчика
в прерывании пишем время его сработки (микросекунд) в “массив[индекс]”, делаем “индекс++” (возможно, следует проверить индекс < макс, если нет - сбросить индекс)
в loop(), если “индекс > 0” (или другой более реалистичный минимум) сравниваем “массив[индекс-1]” и текущее время. Если разница больше некого порога, по которому стоит судить, что пакет закончился - читаем данные из массива, иначе ничего не делаем
Помним, что пакет начинается с high to low, и это первая “единица”, переданная в пакете. Также помним, что каждый новый элемент свидетельствует о смене состояния выхода, и они чередуются. То есть массив[0] порт упал, массив[1] - поднялся и так далее. Четные это падения, нечетные это поднятия. Кстати косвенный признак корректности: у вас пакет заканчивается поднятием вывода, то есть запишется нечетное кол-во элементов, но индекс будет на единицу больше, т.е. “индекс % 2 == 0”
В цикле от “н = 0 до (ВАЖНО) индекс-2” считаем разницу во времени возникновения событий “дельта = массив[н + 1] - массив[н]”. У вас символ “0” или “1” будет “н % 2” (четность индекса), а количество подряд идущих символов = “дельта / длительность одного импульса”
Я бы начал с изучения интернета на тему протоколов. Наверняка уже кто-то разбирался с этой темой.
Как принять: ну, при таких таймингах можно тупо в цикле читать состояние пина. Но для начала лучше разобраться с протоколом, может там что-то стандартное и тогда можно будет выбрать способ приема оптимальный.
ЗЫЖ
Вот тут - Hacking "Chinese" parking sensors - General Guidance - Arduino Forum товарищ использовал UART для приема данных, и, что нехарактерно, что-то там принял.
Если тайминги - сотни миллисекунд, то можно просто читать состояние пина в цикле:
digitalRead(pin);
Можно использовать прерывания, тогда твоя функция будет вызываться при изменении с 0 на 1 и с 1 на 0. В этой функции ты можешь считывать millis() и таким образом ты получишт и значения (1 или 0) и расстояния в миллисекундах между сменами с одного уровня на другой.
Там сотни микросекунд вроде. В первом посте есть скрин.
Так, ставим прерывание по CHANGE
В функции засекаем время между сменой одного уровня на другой.
Как понять где 0 а где 1 ? Тут же не шириной отличается 0 от 1.
я не сталкивался никогда с этим кодированием, но пробежался по вики и понял, что там, не смотря на то, что 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; // сбрасываем индекс
}
//
}
Одной из первых её пробовал.
Точно не помню уже, какой был результат, но не тот что нужно.
Возможно, что-то подстроить надо было, но знаний не хватило (
ощущается как будто-бы более осмысленно. Только uint16_t cnt = dt / bitc; // сколько бит вмещается в это время походу неправильно количество считает. А длина 1 бит в сигнале 200 микросекунд? и период повторения 20000 микросекунд? т.е. 20 миллисекунд