All streams
Search
Write a publication
Pull to refresh
32
0
Send message

Оно не измеряет давление в колёсах, оно измеряет разницу давления в колёсах. То есть, если одно из колёс имеет пониженное или повышенное давление, то внешний его диаметр изменяется, что изменяет частоту его вращения. Эта разница и вычисляется при движении прямо (именно поэтому нужно положение рулевого колеса). А дальше, делается вывод, что вероятность того, что если одно из колёс отличается по частоте вращения от трёх остальных, то вероятнее всего с ним что-то не так - надо проверить давление. Такая штука на корейцах есть, точно знаю на KIA Rio.

Ну так датчики АБС с каждого колеса в шине есть. Осталось дело за углом поворота рулевого колеса, чтобы выверять равномерное движение и высчитывать дельту каждого колеса. Ну а дальше всё считается формулами.

PS: А температуру всё же снижает. Штатно вентилятор включается на 102°С на первую скорость и на 103°С на вторую. Настройка температуры срабатывания на 90°С не позволяет достигнуть сотни градусов.

Действительно, странный вопрос. :)

Ну даже для того, что не "колхозить" - проводку не трогаем, ничего не врезаем, ничего не изменяем.

А управление вентилятором - наверно одно из самых простых и безопасных воздействие на управляемые элементы. Так то можно и бензонасосом управлять, и всеми свечами зажигания, даже обороты мотора задать (чем не круиз-контроль). Но в здравом уме, такое делать вообще опасно.

Поэтому, пусть температура двигателя будет ближе к нормальным условиям. Это и безобидно, и пусть, хоть и немного, но продлевает срок службы патрубков.

Этот код работает в режиме OBD over CAN, то есть использует только диагностические запросы через CAN ID 0x7E0 (отправка) и 0x7E8 (приём) - я предпочёл так не делать. ЭСУД автомобиля постоянно в CAN-шину выбрасывает необходимые мне параметры, поэтому я их просто читаю, декодирую и вывожу. А вот управление исполнительными устройствами - я не нашёл, как это можно сделать непосредственно через CAN-пакеты, да и нет уверенности, что такое возможно. Поэтому для управления использую отправку управляющего пакета через OBD over CAN. Примеры пакетов есть в приведённом коде несколькими комментариями выше: https://habr.com/ru/articles/479672/comments/#comment_27587222

К сожалению, в какой-то версии wxWidgets убрали возможность настройки ширины области захвата разделителя. Связано это было, вроде бы, с тем, что не на всех операционных системах поддерживается управление этим параметром. Получается, что надо попасть в область, выделенную на снимке (там курсор меняется на стрелки вверх-вниз):

Мобильное приложение не планировал, но есть написанное для себя, для проверки различных идей. В данном случае приходится использовать связку CAN-ESP-WiFi-Android (или PC). Заморочка тут в том, что через сеть должны передаваться маленькие порции данных (CAN-пакет около 20 байт). А самих пакетов много 500..1000 в секунду. Сетевой стек в ESP просто захлёбывается в таком режиме. В CAN Sniffer я сделал накопление нескольких CAN-пакетов в один сетевой пакет и только потом отправка. По мне - выглядит это не очень хорошо. Поэтому, тут есть над чем подумать.

Кратко, логика примерно такая:

  1. По прерыванию получаем необходимый пакет(ы), извлекаем из них значение

  2. При превышении заданной температуры включения - отправляем диагностический пакет на включение первой скорости вентилятора

  3. При снижении температуры ниже заданного порога - отправляем диагностический пакет отключения вентилятора (либо передачи управления вентилятором ЭСУД)

Ниже код для примера.

Скрытый текст
#include <mcp2515_can.h>                // CAN_BUS_Shield by Seeed Studio
#include <cppQueue.h>                   // Queue by SMFSW

// Шилд подключен:
// INT  - D2
// CS   - D10
// MOSI - D11
// MISO - D12
// SCK  - D13

// структура хранения данных
struct CANFrame
{
    uint32_t ID;      // идентификатор пакета (4 байта)
    uint8_t Length;   // длина данных (1 байт)
    uint8_t Data[8];  // сами данные (8 байтов)
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// заполнение пакетов данными
// запросить состояние первой скорости вентилятора
static const CANFrame CANLadaFan1Req PROGMEM   = { 0x7E0, 8, { 0x04, 0x2F, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x00 } };
// ответ если выкл                                   7E8, 8,     05,   6F,   00,   0A,   01,   00,   AA,   AA
// ответ если вкл                                    7E8, 8,     05,   6F,   00,   0A,   01,   FF,   AA,   AA
// включить первую скорость вентилятора
static const CANFrame CANLadaFan1On PROGMEM    = { 0x7E0, 8, { 0x05, 0x2F, 0x00, 0x0A, 0x06, 0xFF, 0x00, 0x00 } };
// ответ                                             7E8, 8,     05,   6F,   00,   0A,   06,   FF,   AA,   AA
// отключить первую скорость вентилятора
static const CANFrame CANLadaFan1Off PROGMEM   = { 0x7E0, 8, { 0x05, 0x2F, 0x00, 0x0A, 0x06, 0x00, 0x00, 0x00 } };
// ответ                                             7E8, 8,     05,   6F,   00,   0A,   06,   00,   AA,   AA
// вернуть управление первой скоростью вентилятора ЭСУД
static const CANFrame CANLadaFan1ToECU PROGMEM = { 0x7E0, 8, { 0x05, 0x2F, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00 } };
// ответ                                             7E8, 8,     04,   6F,   00,   0A,   00,   AA,   AA,   AA

// запросить состояние второй скорости вентилятора
static const CANFrame CANLadaFan2Req PROGMEM   = { 0x7E0, 8, { 0x04, 0x2F, 0x00, 0x0D, 0x01, 0x00, 0x00, 0x00 } };
// включить вторую скорость вентилятора
static const CANFrame CANLadaFan2On PROGMEM    = { 0x7E0, 8, { 0x05, 0x2F, 0x00, 0x0D, 0x06, 0xFF, 0x00, 0x00 } };
// отключить вторую скорость вентилятора
static const CANFrame CANLadaFan2Off PROGMEM   = { 0x7E0, 8, { 0x05, 0x2F, 0x00, 0x0D, 0x06, 0x00, 0x00, 0x00 } };
// вернуть управление второй скоростью вентилятора ЭСУД
static const CANFrame CANLadaFan2ToECU PROGMEM = { 0x7E0, 8, { 0x05, 0x2F, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00 } };

// запросить состояние муфты кондиционера
static const CANFrame CANLadaCondReq PROGMEM   = { 0x7E0, 8, { 0x04, 0x2F, 0x00, 0x0B, 0x01, 0x00, 0x00, 0x00 } };
// ответ если выкл                                   7E8, 8,     05,   6F,   00,   0B,   01,   00,   AA,   AA
// ответ если вкл                                    7E8, 8,     05,   6F,   00,   0B,   01,   FF,   AA,   AA
// включить муфту кондиционера
static const CANFrame CANLadaCondOn PROGMEM    = { 0x7E0, 8, { 0x05, 0x2F, 0x00, 0x0B, 0x06, 0xFF, 0x00, 0x00 } };
// ответ                                             7E8, 8,     05,   6F,   00,   0B,   06,   FF,   AA,   AA
// отключить муфту кондиционера
static const CANFrame CANLadaCondOff PROGMEM   = { 0x7E0, 8, { 0x05, 0x2F, 0x00, 0x0B, 0x06, 0x00, 0x00, 0x00 } };
// ответ                                             7E8, 8,     05,   6F,   00,   0B,   06,   00,   AA,   AA
// вернуть управление муфтой кондиционера ЭСУД
static const CANFrame CANLadaCondToECU PROGMEM = { 0x7E0, 8, { 0x05, 0x2F, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00 } };
// ответ                                             7E8, 8,     04,   6F,   00,   0B,   00,   AA,   AA,   AA
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define CAN_INT0   0                    // прерывание 0 на D2
#define CAN_CS     10                   // сигнал CS на D10

volatile bool canDataReceived  = false; // флаг для обработки прерывания
CANFrame canFrame;                      // прочитанный пакет шины
uint8_t  canErrors             = 0;     // счётчик ошибок отправки данных в CAN-шину
#define  CAN_ERRORS_LIMIT        10     // предел количества ошибок CAN-шины, после которого будет произведена попытка сброса CAN-модуля

// основные таймеры и их интервалы
#define  START_DELAY             5000UL // 5 с - пауза до запуска логики
#define  LOGIC_INTERVAL          250UL  // 250 мс - интервал шагов логики
uint32_t startDelayTimer       = 0;     // таймер паузы для запуска логики
uint32_t logicTimer            = 0;     // таймер паузы для логики
uint8_t  step                  = 0;     // номер шага логики

// флаги состояния мотора, кнопки кондиционера и вентилятора
bool   motorOn                 = false; // мотор работает
bool   motorOn_prev            = false; // предыдущее состояние мотора
bool   conditionerOn           = false; // нажата кнопка кондиционера
bool   conditionerOn_prev      = false; // предыдущее состояние кнопки кондиционера
bool   fanOn                   = false; // вентилятор работает
int8_t motorTemperatureFanOn   = 90;    // температура включения первой скорости вентилятора
int8_t motorTemperatureFanOff  = 85;    // температура отключения первой скорости вентилятора
int8_t motorTemperature        = 0;     // текущая температура мотора

cppQueue         canQueue(sizeof(CANFrame));    // очередь сообщений
mcp2515_can      can(CAN_CS);                   // CAN

// Инициализация
void setup()
{
    canInit();
}

// Основной цикл программы
void loop()
{
    uint32_t ms = millis();           // для сохранения текущего времени, чтобы не вызывать millis() несколько раз за цикл

    canRead();

    // обработка логики
    if (ms - logicTimer >= LOGIC_INTERVAL)
    {
        // общие условия
        // если накопилось много ошибок отправки данных в CAN-шину - попытаться сбросить CAN-модуль
        if (canErrors >= CAN_ERRORS_LIMIT)
            step = 0;
        // событие останова мотора - остановить вентилятор
        else if (motorOn_prev && !motorOn)
            step = 30;
        // если мотор не работает - перейти на ожидание запуска
        else if (!motorOn_prev && !motorOn)
            step = 1;

        // переключатель шагов логики
        switch (step)
        {
            case 0:     // инициализация CAN-модуля
                if (canInit())
                    step = 1;
                break;

            case 1:     // ожидание запуска мотора
                if (motorOn)
                {
                    startDelayTimer = ms;
                    step = 2;
                }
                break;

            case 2:     // пауза работы логики после запуска мотора
                if (ms - startDelayTimer >= START_DELAY)
                    step = 10;
                else if (!motorOn)
                    step = 1;
                break;

            ///////////////////////////////////////////////////////////////////////////////////////
            case 10:    // проверка всех условий работы
                // если работает мотор и не работает кондиционер...
                if (motorOn && !conditionerOn)
                    // если температура поднялась...
                    if (motorTemperature >= motorTemperatureFanOn && !fanOn)
                        step = 20;
                    // если температура снизилась...
                    else if (motorTemperature <= motorTemperatureFanOff && fanOn)
                        step = 30;
                break;

            ///////////////////////////////////////////////////////////////////////////////////////
            case 20:    // включить реле вентилятора
                canQueue.push(&CANLadaFan1On);
                step = 21;
                break;
                
            case 21:    // ожидание включения вентилятора
                if (fanOn)
                    step = 10;
                else
                    step = 20;
                break;

            ///////////////////////////////////////////////////////////////////////////////////////
            case 30:    // отключить реле вентилятора
                canQueue.push(&CANLadaFan1Off);
                step = 31;
                break;

            case 31:    // ожидание отлючения вентилятора
                if (!fanOn)
                    step = 10;
                else
                    step = 30;
                break;
                
            default:
                step = 1;
                break;
        }

        logicTimer = ms;
        motorOn_prev = motorOn;
        conditionerOn_prev = conditionerOn;

        // отправка пакетов в CAN-шину
        if (!canQueue.isEmpty())
        {
            CANFrame canFrame;
            canQueue.pop(&canFrame);
            canSendFrame(canFrame);
        }
    }
}

// Инициализация CAN-модуля
bool canInit()
{
    canQueue.clean();
    pinMode(CAN_INT0, INPUT_PULLUP);
    detachInterrupt(digitalPinToInterrupt(CAN_INT0));
    bool canOk = false;
    canErrors = 0;
    canDataReceived = false;
    fanOn = false;
    // подключить обработчик прерывания о поступлении данных по спадающему уровню сигнала
    attachInterrupt(digitalPinToInterrupt(CAN_INT0), canInterrupt, FALLING);
    
    // настройка скорости обмена и частоты кварца для CAN-шины
    if (can.begin(CAN_500KBPS, MCP_8MHz) == CAN_OK)
    {
        canOk = true;

        // типы фильтров и масок
        #define STD 0
        #define EXT 1
        
        // установка фильтров на необходимые пакеты
        can.init_Filt(0, STD, 0x0);
        can.init_Filt(1, STD, 0x0);
        can.init_Filt(2, STD, 0x0);
        can.init_Filt(3, STD, 0x0);
        can.init_Filt(4, STD, 0x0);
        can.init_Filt(5, STD, 0x0);
        
        // установка масок
        can.init_Mask(0, STD, 0x0);
        can.init_Mask(1, STD, 0x0);
    }

    return canOk;
}

// Обработчик прерывания поступивших данных
void canInterrupt()
{
    // установить флаг наличия данных в буфере
    canDataReceived = true;
}

// Чтение данных из CAN-шины
void canRead()
{
    // если в буфере есть принятые данные...
    if (canDataReceived)
    {    
        canDataReceived = false;
        
        while (can.checkReceive() == CAN_MSGAVAIL)
        {
            // чтение данных в буфер
            if (can.readMsgBuf(&canFrame.Length, canFrame.Data) == CAN_OK)
            {
                // получение идентификатора пакета (достоверный только после чтения сообщения в буфер)
                canFrame.ID = can.getCanId();
                // обработать пакет
                canProcessFrame();
            }
        }
    }
}

// Обработчик поступивших данных
void canProcessFrame()
{
    switch (canFrame.ID)
    {
        case 0x180:     // моторный пакет - мотор работает если есть обороты мотора
            float motorRpm = (float)(word(canFrame.Data[0], canFrame.Data[1]) >> 3);      // деление на 8.0
            motorOn  = (motorRpm > 500.0F);
            break;
    
        case 0x551:     // если это кадр с содержанием информации о температуре мотора (8 байтов)
            // температура представлена со смещением в +40°C
            motorTemperature = (int8_t)canFrame.Data[1] - 40;
            break;    
        
        case 0x555:     // пакет от САУКУ (5 байтов)
            conditionerOn = canFrame.Data[2] & 0x40;
            break;
    
        case 0x7E8:     // если это кадр ответа ЭСУД на запрос - расшифровать его и включить индикатор
            if (canFrame.Data[0] == 0x05 &&
                canFrame.Data[1] == 0x6F &&
                canFrame.Data[2] == 0x00 &&
                canFrame.Data[3] == 0x0A &&
                canFrame.Data[4] == 0x06)
            {
                // ответ ЭСУД на управление вентилятором 1
                fanOn = (canFrame.Data[5] == 0xFF);
            }
            break;
    }
}

// Отправить CAN-пакет
void canSendFrame(CANFrame& outFrame)
{
    if (can.sendMsgBuf(outFrame.ID, 0, outFrame.Length, outFrame.Data) != CAN_OK)
    {
        canErrors++;
    }
}

По блоку M74CAN все основные параметры довольно легко декодируются. Я себе собрал табличку со всеми найденными параметрами.
Коды ошибок - их нет в CAN шине, они в отдельном протоколе OBD поверх CAN. В статье я об этом написал. Для диагностики часто используются адреса 0x7E0, 0x7E8.
Я делал себе программное управление вентилятором охлаждения, некий TempGuard: включаю вентилятор на 90°C, выключаю при 85°C (параметры настраиваются). Причём включаю первую скорость вентилятора - она и потише, да и хватает её даже в жару. Нюанс в том, что не надо управлять вентиляторами, когда включен кондиционер, там сам ЭБУ будет этими вентиляторами управлять. В статье, последняя анимация и параграф над ней - как раз пример управления выходами ЭБУ (первая скорость вентилятора, муфта кондиционера).
Так же делал программу для телефона, которая различные параметры выводит, графики скорости, оборотов, положения педали газа. Читает всё это через Wi-Fi реализованный через ESP.
PS: Добавил схемы подключения AVR и ESP в проект для микроконтроллера.

Начиная с версии 2.2.0 приложение собирается в Linux. Проверял в Ubuntu с подключением к железу.

Ноутбук жив. Находится в пользовании у родственника. Режим эксплуатации - крайне-лёгкий.

https://leroymerlin.ru/company/nizkiye-ceny-kazhdiy-den/

В первом блоке справа (цитата):
"КАЖДЫЙ МАГАЗИН В КАЖДОМ ГОРОДЕ РЕГУЛЯРНО ИЗУЧАЕТ ЦЕНЫ НА РЫНКЕ, ЧТОБЫ СДЕЛАТЬ НАШЕ ПРЕДЛОЖЕНИЕ ДОСТУПНЫМ И ВЫГОДНЫМ."

Так что зависит от цен в других городских магазинах.

Вижу публикацию, беру ссылку, но не работает...


Не смог победить. На канале AlphaCentauri заметка об аварийной ситуации при стыковке.

Рекорд — рекордом. Главное, чтобы всё хорошо было. А то вдруг:
Кажется, у нас нештатная ситуация на МКС...

PS: Надеюсь, что им только кажется.
Известные личности. На всякий случай снова посмотрю их заметки.
Ещё читал сигнал с датчиков АБС с каждого колеса, просто прирастающий счётчик, когда колесо вращается. Но тоже сразу номер не припомню.
Именно так, вот неполный список:
0x160 — педаль газа, дроссель
0x180 — обороты мотора, некоторые параметры по мотору
0x280 — приборы, скорость
0x481 — двери, световые приборы, подогревы
0x551 — приборы
0x555 — климат-контроль
0x560 — АКП

АБС точно не помню, кажется в районе 0x35x.
Спасибо. Приятно.
Так и не метил я в профессиональную разработку с этим и хаб про «сделай сам». Хотя очень всё это интересно.
PID-ы стандартизованы в OBD, их описание есть в Wikipedia, но лучше смотреть английскую версию, там больше подробностей. И эти PID-ы к CAN шине не имеют прямого отношения. CAN шина живёт отдельно со своими данными и протоколом. А вот PID-ы в диагностическом режиме (эдакая замена K-line) внутри пакетов CAN 0x7E0 и 0x7E8.

Information

Rating
Does not participate
Location
Высокая Гора, Татарстан, Россия
Registered
Activity