Замер разгона автомобиля на arduino (Wemos D1)

В одной из прошлых частей мы собрали простой спидометр с возможностью замера разгона до сотни, сегодня мы расширим его функционал. На борту платы Wemos D1, благодаря чипу ESP8266, есть полноценный WiFi, так давайте же задействуем его, создадим web сервер и будем передавать результаты замеров на телефон.

Подключение

Подключаем все модули абсолютно также, как в прошлый раз:

  1. Соединим дисплей с шиной I2C: VCC -> 3V3, GND -> GND, SCL -> D1, SDA -> D2.
  2. А GPS модуль с программным UART: V -> 3V3, G -> GND, R -> D8, T -> D7.

Web сервер на ESP8266

В этот раз мы выжмем максимум из NEO-7, в настройках зададим рейт обновления 16 раз в секунду, минимум 5 спутников для фиксации и автомобильный режим. Эти настройки позволяют повысить точность замеров.


Создать web-сервер на ESP8266 проще простого. Необходимо указать на каком порту он будет ждать подключения, для http это традиционно 80й порт, и указать, по каким запросам, какую функцию выполнять. Я подготовил небольшую html страничку, которая будет запрашивать последние 10 результатов замеров и отображать их на экране. Для максимального быстродействия результаты будут отправляться в формате json.

const char *ssid     = "ARDULOGIC";  // Название сети WiFi
const char *password = "1234567890"; // Пароль для подключения

ESP8266WebServer server(80);         // Создаем веб сервер на 80 порту

void ALServer::serverHandle()
{
    server.handleClient(); // Ждём подключения
}

void ALServer::createAP()
{
    WiFi.softAP(ssid, password); // Создаём точку WiFi
    // Указываем по каким роутам какие методы запускать
    server.on("/", [this]() {
        handleRoot();
    });
    server.on("/style.css", [this]() {
        handleStyle();
    });
    server.on("/results", [this]() {
        handleResults();
    });
    server.begin();
}

// Метод формирует стартовую страницу http://192.168.4.1
void ALServer::handleRoot()
{
    server.send(200, "text/html", data_indexHTML);
}

void ALServer::handleStyle() {
    server.send(200, "text/css", data_styleCSS);
}

void ALServer::handleResults()
{
    StaticJsonBuffer<2000> jsonBuffer;
    JsonArray& arrayJ = jsonBuffer.createArray();
    // Результат будет отправлять в формате json
    for (int i = 0; i < _n; i++) {
        if (0.0 != _meterings[i].accel30) {
            JsonObject& object = jsonBuffer.createObject();
            dtostrf(_meterings[i].accel30, 3, 1, buf30);
            object["a30"] = (String)buf30;
            dtostrf(_meterings[i].accel60, 3, 1, buf60);
            object["a60"] = (String)buf60;
            dtostrf(_meterings[i].accel100, 3, 1, buf100);
            object["a100"] = (String)buf100;
            arrayJ.add(object);
        }
    }
    char buffer[512];
    arrayJ.printTo(buffer, sizeof(buffer));
    server.send(200, "text/html", buffer);
}

Скачать архив со скетчем: Speedometer3.zip

Теперь если подключиться к устройству по WiFi, после замера можно увидеть такую картину.


Дисплей

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

struct FullData
{
    unsigned int numSV;  // Спутники         
    float hAcc;          // Точность
    char gpsTime[9];     // Время
    char bufLatitude[10];
    char bufLongitude[10];
    unsigned int gSpeedKm = 0; // Скорость
};

void LCD::updateScreen(FullData* fullData, Metering* metering)
{
    // Рисуем линии "сигнала" gps
    display->clear();
    display->drawVerticalLine(7, 2, 8);
    display->drawVerticalLine(6, 2, 8);
    display->drawVerticalLine(4, 5, 5);
    display->drawVerticalLine(3, 5, 5);
    display->drawVerticalLine(1, 8, 2);
    display->drawVerticalLine(0, 8, 2);
    display->drawHorizontalLine(0, 11, 128);
    display->drawHorizontalLine(0, 54, 128);
    // Спутники                
    display->setTextAlignment(TEXT_ALIGN_LEFT);
    display->setFont(ArialMT_Plain_10);
    display->drawString(11, 0, (String)fullData->numSV);
    // Точность
    display->setTextAlignment(TEXT_ALIGN_LEFT);
    display->setFont(ArialMT_Plain_10);
    display->drawString(42, 0, (String)fullData->hAcc);
    // Время
    display->setTextAlignment(TEXT_ALIGN_RIGHT);
    display->setFont(ArialMT_Plain_10);
    display->drawString(128, 0, fullData->gpsTime);
    // Разгон до 30 км/ч
    display->setTextAlignment(TEXT_ALIGN_RIGHT);
    display->setFont(ArialMT_Plain_16);
    dtostrf(metering->accel30, 3, 1, buf30);
    display->drawString(122, 10, (String)buf30);
    // Разгон до 60 км/ч
    display->setTextAlignment(TEXT_ALIGN_RIGHT);
    display->setFont(ArialMT_Plain_16);
    dtostrf(metering->accel60, 3, 1, buf60);
    display->drawString(122, 24, (String)buf60);
    // Разгон до 010 км/ч
    display->setTextAlignment(TEXT_ALIGN_RIGHT);
    display->setFont(ArialMT_Plain_16);
    dtostrf(metering->accel100, 3, 1, buf100);
    display->drawString(122, 38, (String)buf100);
    // Координаты
    display->setTextAlignment(TEXT_ALIGN_LEFT);
    display->setFont(ArialMT_Plain_10);
    display->drawString(0, 54, fullData->bufLatitude);
    display->setTextAlignment(TEXT_ALIGN_RIGHT);
    display->setFont(ArialMT_Plain_10);
    display->drawString(128, 54, fullData->bufLongitude);
    // Скорость    
    char gpsSpeed[3];
    sprintf(gpsSpeed, "%03d", fullData->gSpeedKm);
    display->setTextAlignment(TEXT_ALIGN_CENTER);
    display->setFont(Orbitron_Light_30);
    display->drawString(45, 20, gpsSpeed);
    display->display();
}

Вот что у нас получится в итоге.