faculty_project/Проектирование встроенных приложений Практическая работа 2 _вер.4_.docx

185 KiB
Raw Permalink History

Практическая работа2.

ОБМЕН ДАННЫМИ С ДАТЧИКАМИ

Цель работыиспользование цифровыхи аналоговых датчиков, получение и обработка в составе учебного комплекса.

ОБЩИЕ СВЕДЕНИЯ

Датчикив составе лабораторного макета могут подключаться к цифровым портам, последовательным интерфейсам (UART, SPI, I2C) или к входу АЦП,которые связаны электрически с определенными входами/выходами микроконтроллера и не могут быть перенесены на другие порты ввода-вывода.

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

На рисунке 1 приведен пример подключения ведущего (Master) и ведомого (Slave) устройств при помощи двухпроводной шины I2C. Используется две линии. Линия данных (Serial Data Line, SDA) и линия тактовой синхронизации сигнала (Serial Clock Line, SCL). Временные диаграммы работы шины приведены на рисунке 2. К шине может быть подключено больше двух устройств, как в режиме мастер, так и в режиме ведущий. Цифровые датчики используют общую шину данных. Отношения между устройствами классически принадлежат к классу: ведущий ведомый, когда ведущий опрашивает все ведомые устройства и сам устанавливает промежутки времени в которых производится опрос.

Рис. 2. Временные диаграммы шины I2C.

Основа передачи данных на шине побитовая трансляция пакетов после появления на линиях состояния старт (StartCondition) перехода линии SDA в низкое положениепри высоком уровне на линии SCL. Далее промежуток готовности и последующая передача данных в заданном режиме, чаще всего 7 информационных бит и один бит четности, для проверки целостности информации. На рисунке показана работа шины частично (нет условия стоп, адресации, подтверждения от приемника)? Да это сам пакет данные, передача. Так как мы сами не будем писать интерфейс то считаю избыточным добавлять такие же по размеру рисунки , чтобы объяснять глубже.

В лабораторном макетеMaster-устройство– это микроконтроллер, а Slave-устройства датчики.

Последовательность действий для получения данных цифрового датчика по шине I2C:

инициализация датчиков на шине;

–вызов-приглашение определенного датчика на шине по его фиксированному адресу устройства;

получение подтверждения от подчиненного устройства о готовности принять команду (т.е. подчиненное устройство присутствует на шине);

отправка мастером кода команды, для запроса данных от датчика

переход мастера в режим подчиненного и ожидания данных от опрашиваемого датчика

прием данных байт от датчика

завершение приема данных

автоматическая обработка данных, для представления в удобном виде средствами высокоуровневой библиотеки

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

Основной скетч, будущего макета приведен в Приложении 1. Для выполнения лабораторной работы нужно иметь при себе листинг скетча. Подготовкой для выполнения лабораторной работы является обновление знаний по языку С++ и его основными синтаксическими конструкциями.

  1. АНАЛИЗ МАСТЕР-СКЕТЧА

Для выполнения первого пункта лабораторной работы проведите анализ скетча (см. Приложение 1).Следуйте предложенной последовательности действий или скорректируйте ход данной работы совместно с преподавателем.

  1. Проанализируйте листинг, разбейте его на логические блоки. Для каждого блока, выделенного вами, ответьте на вопрос: какую функцию выполняет этот блок?

  2. Найдите в коде блок инициализации каждого из устройств, подключенных к микроконтроллеру. Чаще всего для скетчей данного типа инициализация выглядит как последовательность шагов: подключение библиотеки, создание основного элемента объекта, задание первичных свойств объекта, запуск взаимодействие с объектом датчиков в блоке Setup.

  3. Найдите участки кода, отвечающие за коммуникацию с каждым из датчиков, получение показаний с них.

  4. Подготовьте выборки из основного скетча, для работы с каждым из датчиков. Для выполнения этого задания необходимо вспомнить основы С++ в части основных синтаксических конструкций, правил работы с областью видимости, созданию собственных функций, создания собственных классов. (вспоминать из предыдущих курсов?) Да. Та как это не пособие по с++. Либо если придем к выводу, что нужно дать «основы» то на примере этого пункта разбор мастер-скетча, можно «пробежаться» по основным конструкциям С++"

  1. РАБОТА С ДАТЧИКАМИ

В ходе выполнения текущего пункта используйте элементы кода, приготовленные в предыдущем разделе:

  1. Напишите скетч для выборки данных датчика и вывода их в монитор порта, с периодичностью 300-500 мс;

  2. Комбинируйте разрозненные скетчи в единый, для работы со всеми датчиками единовременно. Сделайте вывод о занимаемой памяти микроконтроллера (почему это нужно в данном пункте?). Для того чтобы подготовить листинг кода, для разбиения на функции вызова отдельных датчиков их опроса и измерения работы скетча, временных интервалов.Т.е. это как подпункт «составьте единый скетч работы со всеми датчиками и измерьте время работы его.

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

После выполнения кода модифицируйте скетчи, ответьте на вопросы:

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

  2. Сколько времени занимает вывод дынных после снятия показаний в монитор порта? Занесите данные в таблицу.

  3. Сколько времени необходимо для обработки 100 отсчетов с каждого из датчиков и вывод в монитор порта. Занесите данные в таблицу и постройте нормированные графики для этого и первого вопроса данного пункта.

Вопросы по пунктам 1-3.

- как определяются времена опроса, вывода и обработки? ;

ОТВЕТ: опрос от начала вызова опроса, до присвоения переменной значения, вывода от присвоения переменной значения до завершения выполнения функции печати в порт вывода, обработки от начала цикла агрегации , до конца цикла агрегации

- как должна выглядеть таблица?.

ОТВЕТ если нужен пример таблицы то он вот он. Составляется в совобном режиме студенты справились с этим заданием хорошо. и обработали дома данные верно. т.е. эти пункты сильных вопросов не принесли.

Таблица 1.1. Протокол измерений временных интервалов опроса датчиков.

Название датчика Измеряемая величина Действие Кол-во опросов, вызовов Затраченное время, мсек
1 BMP-180 давление опрос 100 17
2 BMP-180 давление вывод данных - 187мксек
  1. Произведите обработку данныхс усреднением, отбросом выбросов по критерию 3τ, вычислением медианы, динамического диапазона для данных каждого датчика(какое количество значений использовать для обработки и в каком виде представляются результаты?).

ОТВЕТ 100 значений значений как указано в п.п.2. Результаты представить в виде таблицы , для каждого из датчиков.

  1. РАБОТА СО СВЕТОДИОДНОЙ ЛИНЕЙКОЙ

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

Цифровые светодиоды соединены последовательно в линию. Передача данных идет от управляющего микроконтроллерак первому светодиоду, при передачи последующей пачки данных, первая будет вытеснена на линию данных следующего светодиод и принята им, и т.д., пока все светодиоды не получат данные для отображения, после чего микроконтроллер передает команду к отображению данных. Рисунок 3 показывает выводы отдельного светодиода типа WS2812, а рисунок 4 типовую схему их последовательного подключения.

Рис.4. Схема подключение светодиодов WS2812.

После выполнения кода модифицируйте скетчи для выполнения заданий:

  1. Отобразите текущую температуру с датчика температуры цветом целой светодиодной линейки. Стоит настроить градацию света с большой чувствительностью, так как нагреть датчик удастся на диапазон температур +20 / +60 градусов, учтите это для составления функции отображения значений.

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

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

Контрольные вопросы

  1. Приведите пример последовательных шин коммуникации.

  2. Кто и устройств на шине I2C инициирует передачу данных?

  3. Для чего в проекте используется библиотека FASTled ?

  4. Поясните принцип работы цифровой светодиодной линейки.

  5. Представьте обобщенный список действий для получения данных от цифрового датчика на шине I2C.

Используемые источники:

  1. https://rheingoldheavy.com/i2c-signals/

  2. https://www.circuitbasics.com/basics-of-the-i2c-communication-protocol/

  3. Шиллер. «Искусство радиотехники.» Москва 2007г 342 стр.

  4. https://www.google.com/search?q=ws1812+diagram&tbm=isch&hl=ru&sa=X&ved=2ahUKEwjZxcmR6KT9AhVOsyoKHXlVCNgQBXoECAEQJA&biw=2560&bih=1297#imgrc=T_ruE7CdS5Zb4M

  5. https://cdn-shop.adafruit.com/datasheets/WS2812.pdf

Приложение 1. Мастер скетч

//checkthepinoutbeforuse

#define PIN_LED1 12

#define PIN_LED2 13

#define PIN_LED3 15

#define PIN_LED4 7

#define PIN_LED5 6

#define PIN_LIGHT A0

#define PIN_BUTT1 14

#define PIN_BUTT2 0

#define PIN_BUTT3 2

#define PIN_BUTT4 11

#define PIN_BUTT5 8

#define DATA_FASTLED_PIN 16

//Led strip init

#define FASTLED_ESP8266_RAW_PIN_ORDER

#include <FastLED.h>

#define NUM_LEDS_IN_STRIPLINE 8

CRGB ledsLine[NUM_LEDS_IN_STRIPLINE];

//pressure sensor init

//include wire - Spi bus

#include <Wire.h>

#include <SFE_BMP180.h>

SFE_BMP180 pressureSens;

struct BMP180val{

double temperature,

pressure;

};

//init axelerometer sensor

#include <Adafruit_Sensor.h>

#include <Adafruit_ADXL345_U.h>

/* Assign a unique ID to this sensor at the same time */

Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12346);

//init guest sensor

#include <SparkFun_APDS9960.h>

// Global Variables

SparkFun_APDS9960 apds = SparkFun_APDS9960();

#include "StandData.h"

StandData standData;

#include <ESP8266WiFi.h>

#include <WiFiClient.h>

#include <ESP8266WebServer.h>

#include <ESP8266mDNS.h>

ESP8266WebServer server(80);

const char* ssid = "TP-LINK_6EF532";

const char* password = "49344059";

const char* mdnsName = "esp8266";

//Index HTML page

const String IndexPage = "<html>\

<head>\

<title>ESP8266 Web Server POST handling</title>\

<style>\

body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\

</style>\

</head>\

<body>\

<h1>POST plain text to /postplain/ route</h1>\

<form method=\"post\" enctype=\"text/plain\" action=\"/postvalue\">\

<input type=\"text\" name=\"json\" value=\'{JSON string}\'>\

<br><br>\

<input type=\"text\" name=\"text\" value=\'\'>\

<input type=\"submit\" name=\"button\" value=\"Send string\" >\

</form>\

</body>\

</html>";

void getMain ()

{

server.send(200, F("text/html"), IndexPage);

BlinkPWRled(4, 40);

}

void getSensVal ()

{

standData.LED1 = digitalRead(PIN_LED1);

standData.LED2 = digitalRead(PIN_LED2);

standData.LED3 = digitalRead(PIN_LED3);

standData.LED4 = digitalRead(PIN_LED4);

standData.LED5 = digitalRead(PIN_LED5);

standData.button1State = digitalRead(PIN_BUTT1);

standData.button2State = digitalRead(PIN_BUTT2);

standData.button3State = digitalRead(PIN_BUTT3);

standData.button4State = digitalRead(PIN_BUTT4);

standData.button5State = digitalRead(PIN_BUTT5);

standData.lightness = analogRead(PIN_LIGHT);

BMP180val tmp = GetBMP180Val();

standData.temperature = tmp.temperature;

Serial.println(standData.temperature);

standData.pressure = tmp.pressure;

Serial.println(standData.pressure);

if(!apds.readAmbientLight(standData.ambient_light) ||

!apds.readRedLight(standData.red_light) ||

!apds.readGreenLight(standData.green_light) ||

!apds.readBlueLight(standData.blue_light) )

Serial.println("Error reading light values");

//for axeleration

sensors_event_t event;

accel.getEvent(&event);

standData.acceleration_x = event.acceleration.x;

standData.acceleration_y = event.acceleration.y;

standData.acceleration_z = event.acceleration.z;

for(int i = 0; i < NUM_LEDS_IN_STRIPLINE; i++)

standData.stripLedsColors[i] = ledsLine[i];

String jsonMassage = standData.GetJSONString();

server.send(200, F("text/plain"), jsonMassage);

BlinkPWRled(4, 40);

}

void postSensValue() {

if (server.method() != HTTP_POST) {

server.send(405, "text/plain", "Method Not Allowed");

} else {

server.send(200, "text/plain", "POST body was:\n" + server.arg("plain"));

String json = server.arg("plain");

Serial.print("args : "); Serial.println(json);

//find a JSON part

int startIndex = json.indexOf("json=");

int stopIndex = json.indexOf("text=");

Serial.println(startIndex);

Serial.println(stopIndex);

String JSONsubString = json.substring(startIndex+5,stopIndex);

Serial.print("JSON sub string: "); Serial.println(JSONsubString);

StaticJsonDocument<700> jsonDocument;

deserializeJson(jsonDocument, JSONsubString);

standData.LED1 = jsonDocument["LED1"];

digitalWrite(PIN_LED1, standData.LED1);

standData.LED2 = jsonDocument["LED2"];

digitalWrite(PIN_LED2, standData.LED2);

standData.LED3 = jsonDocument["LED3"];

digitalWrite(PIN_LED3, standData.LED3);

standData.LED4 = jsonDocument["LED4"];

digitalWrite(PIN_LED4, standData.LED4);

JsonArray color_r = jsonDocument["color_r"];

Serial.println(color_r);

//TODO to optimize

int i = 0;

for(JsonVariant v : color_r)

{

standData.stripLedsColors[i].r = v.as<int>();

i++;

}

i = 0;

JsonArray color_g = jsonDocument["color_g"];

Serial.println(color_g);

//TODO to optimize

i = 0;

for(JsonVariant v : color_g)

{

standData.stripLedsColors[i].g = v.as<int>();

i++;

}

i = 0;

JsonArray color_b = jsonDocument["color_b"];

Serial.println(color_b);

//TODO to optimize

i = 0;

for(JsonVariant v : color_b)

{

standData.stripLedsColors[i].b = v.as<int>();

i++;

}

//show a new line colors

for(int i = 0; i < NUM_LEDS_IN_STRIPLINE; i++)

{

ledsLine[i] = standData.stripLedsColors[i];

}

FastLED.show();

}

BlinkPWRled(4, 40);

}

// Define routing

void restServerRouting() {

//return HTML page

server.on("/", HTTP_GET, getMain);

//return JSON data value representation

server.on(F("/sensval"), HTTP_GET, getSensVal);

server.on(F("/hello"), HTTP_GET, hallo);

//get JSON value for parsing

//return actual data value (sens and LEDs)

server.on("/postvalue", HTTP_POST, postSensValue);

}

void hallo()

{

server.send(200, "text/plain", "Hallo");

BlinkPWRled(4, 40);

}

// Manage not found URL

void handleNotFound() {

String message = "File Not Found\n\n";

message += "URI: ";

message += server.uri();

message += "\nMethod: ";

message += (server.method() == HTTP_GET) ? "GET" : "POST";

message += "\nArguments: ";

message += server.args();

message += "\n";

for (uint8_t i = 0; i < server.args(); i++) {

message += " " + server.argName(i) + ": " + server.arg(i) + "\n";

}

server.send(404, "text/plain", message);

Serial.println("error query from client");

Serial.println(millis());

BlinkPWRled(4, 40);

}

void writeLog(){

static unsigned long t_in = 0;

static const int decay_sec = 2; //write log each

if(t_in + decay_sec * 1000 < millis())

{

t_in = millis();

Serial.print("Log time: "); Serial.print(t_in / 1000 /60); Serial.print(":"); Serial.println(t_in / 1000 % 60);

}

}

void LedAlarmBlink()

{

while(1)

{

digitalWrite(PIN_LED5, !digitalRead(PIN_LED1));

delay(400);

}

}

BMP180val GetBMP180Val(){

BMP180val tmp;

char status;

status = pressureSens.startTemperature();

if (status != 0)

{

delay(status);

status = pressureSens.getTemperature(tmp.temperature);

if (status != 0)

{

status = pressureSens.startPressure(3);

if (status != 0)

{

delay(status);

status = pressureSens.getPressure(tmp.pressure, tmp.temperature);

if (status == 0)

Serial.println("error retrieving pressure measurement\n");

}

else Serial.println("error starting pressure measurement\n");

}

else Serial.println("error retrieving temperature measurement\n");

}

else Serial.println("error starting temperature measurement\n");

return tmp;

}

void BlinkPWRled(int blinkNum, int decay = 200)

{

if(blinkNum < 2) blinkNum = 2;

for(int i = 0; i < blinkNum; i++)

{

digitalWrite(PIN_LED5, 1);

delay(decay);

digitalWrite(PIN_LED5, 0);

delay(decay);

}

}

void setup(void) {

pinMode(PIN_LED5, OUTPUT);

BlinkPWRled(5);

Serial.begin(115200);

while (!Serial);

BlinkPWRled(2);

WiFi.mode(WIFI_STA);

WiFi.begin(ssid, password);

Serial.println("Start connnect");

// Wait for WiFi connection

while (WiFi.status() != WL_CONNECTED) {

delay(100);

Serial.print(".");

}

BlinkPWRled(4, 100);

Serial.print("Connected to "); Serial.println(ssid);

Serial.print("IP address: "); Serial.println(WiFi.localIP());

// Activate mDNS this is used to be able to connect to the server

// with local DNS hostmane esp8266.local

if (MDNS.begin("esp8266")) Serial.println("MDNS responder started");

MDNS.addService("http", "tcp", 80);

// Set server routing

restServerRouting();

// Set not found response

server.onNotFound(handleNotFound);

// Start server

server.begin();

Serial.println("HTTP server started");

delay(500);

BlinkPWRled(2, 300);

//////SENS INIT/////

pinMode(PIN_LED1, OUTPUT);

pinMode(PIN_LED2, OUTPUT);

pinMode(PIN_LED3, OUTPUT);

pinMode(PIN_LED4, OUTPUT);

pinMode(PIN_BUTT1, INPUT_PULLUP);

pinMode(PIN_BUTT2, INPUT_PULLUP);

pinMode(PIN_BUTT3, INPUT_PULLUP);

pinMode(PIN_BUTT4, INPUT_PULLUP);

pinMode(PIN_BUTT5, INPUT_PULLUP);

//Led strip init

FastLED.addLeds<WS2812B, DATA_FASTLED_PIN, RGB>(ledsLine, NUM_LEDS_IN_STRIPLINE);

//global lightness

FastLED.setBrightness(64);

/*

CRGB color = CRGB(r, g, b);

CHSV color = CHSV(h, s, v);

ledsLine[i] = CRGB::Red;

*/

//pressure init

if (!pressureSens.begin())

{

// Oops, something went wrong, this is usually a connection problem,

// see the comments at the top of this sketch for the proper connections.

Serial.println("BMP180 init fail (disconnected?)\n\n");

LedAlarmBlink();

}

//axeleration Sens init

if(!accel.begin())

{

Serial.println("Ooops, no ADXL345 detected ... Check your wiring!");

LedAlarmBlink();

}

accel.setRange(ADXL345_RANGE_16_G);

//Guest sensor init

if ( !apds.init() )

{

Serial.println(F("Something went wrong during APDS-9960 init!"));

LedAlarmBlink();

}

// Start running the APDS-9960 light sensor (no interrupts)

if ( !apds.enableLightSensor(false) )

{

Serial.println(F("Something went wrong during light sensor init!"));

LedAlarmBlink();

}

// Wait for initialization and calibration to finish

delay(200);

digitalWrite(PIN_LED5, 1);

Serial.println("Setup continue");

}

void loop(void) {

server.handleClient();

MDNS.update();

writeLog();

}