246   222   68    
04.07.2019   Уроки   Теги статьи #, #

Подключение нескольких датчиков к одному последовательному порту Ардуино

Перейти к комментариям ↓

В этом уроке рассмотрим, как подключить несколько датчиков Atlas к одному последовательному порту Arduino.

Проект

В этом уроке мы будем расширять один последовательный порт Arduino UNO UART (Rx/Tx) так, чтобы можно было подключить несколько датчиков Atlas. Расширение осуществляется с помощью платы расширения последовательного порта 8:1 (англ. название - 8:1 Serial Port Expander). Порт Arduino подключается к "расширителю", после чего сигнал направляется на восемь портов, к которым подключены периферийные устройства. Для простоты мы будем использовать три порта, но с помощью нескольких дополнительных шагов вы можете сделать расширение, чтобы использовать все восемь.

Связь осуществляется через режим UART, а результаты отображаются на последовательном мониторе Arduino. По умолчанию показания подключенных датчиков опрашиваются непрерывно. Затем можно открыть отдельные каналы, что позволит пользователю общаться с определенным датчиком. Преимущества такого подхода:

  • Разворачивание одного последовательного порта UART (Rx/Tx) на восемь дополнительных портов.
  • Легко следить за тем, какой канал открывается с помощью встроенных светодиодов на модуле расширения.
  • Работает со следующими датчиками Atlas EZO: pH, соленость, растворенный кислород (DO), температура, окислительно-восстановительный потенциал (ORP), диоксид углерода (CO2), перистальтический насос.
  • Выходной сигнал датчика в реальном времени и взаимодействие с пользователем.

Комплектующие

Компоненты оборудования, которые нам будут нужны в этом уроке:

  • Arduino UNO × 1
  • Плата расширения × 1
  • Макет (универсальный) × 1
  • Датчик растворенного кислорода × 1
  • Датчик солености × 1
  • Датчик pH × 1
  • Перемычки × 1

Программные приложения:

Схема соединения

Соберите все комплектующие согласно схеме выше. Убедитесь, что датчики находятся в режиме UART, прежде чем подключать их к расширителю. Чувствительность датчиков - это то, что дает им высокую точность. Но это также означает, что они подвержены помехам от другой электроники и необходима электрическая изоляция. Изоляторы напряжения используются для изоляции датчиков растворенного кислорода и pH от датчика солености. Без изоляторов показания ошибочны.

Программа Arduino

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

  1. Загрузите Ezo_uart_lib, zip-папку с GitHub на свой компьютер.
  2. На вашем компьютере откройте IDE Arduino (вы можете скачать IDE здесь, если она у вас еще не установлена).
  3. В IDE перейдите в:
    # Sketch -> Включить библиотеку -> Добавить ZIP-библиотеку (Sketch -> Include Library -> Add.ZIP Library)
    Выберите папку Ezo_uart_lib, которую вы только что загрузили. Соответствующие файлы теперь включены.
  4. Скопируйте код из Serial_port_expander_example на рабочую панель IDE. Вы также можете получить к нему доступ из папки Ezo_uart_lib, загруженной выше.
  5. Скомпилируйте и загрузите код Serial_port_expander_example в Arduino Uno.
  6. Последовательный монитор используется в качестве канала связи. Чтобы открыть последовательный монитор, перейдите в:
    # Инструменты -> Последовательный монитор (Tools -> Serial Monitor)
    или нажмите Ctrl + Shift + M на клавиатуре. Установите скорость передачи 9600 и выберите «Возврат каретки» (англ - carriage return). Показания датчика теперь должны постоянно отображаться и пользователь сможет взаимодействовать с отдельными датчиками.
//This code works similarly to the serial port expander sample code in terms of the interface
//but constantly polls all the circuits by default
//To open a channel (marked on the board as P1 to P8) send the number of the channel followed by a colon and the command (if any) that you want to send. End the string with a carriage return.
//1:r<CR>
//2:i<CR>
//3:c<CR>
//4:r<CR>

//To open a channel and not send a command just send the channel number followed by a colon.
//1:<CR>
//3:<CR>

#include <Ezo_uart.h>
#include <SoftwareSerial.h>                           //we have to include the SoftwareSerial library, or else we can't use it
#define rx 2                                          //define what pin rx is going to be
#define tx 3                                          //define what pin tx is going to be
SoftwareSerial myserial(rx, tx);                      //define how the soft serial port is going to work

int s1 = 6;                                           //Arduino pin 6 to control pin S1
int s2 = 5;                                           //Arduino pin 5 to control pin S2
int s3 = 4;                                           //Arduino pin 4 to control pin S3
int port = 1;                                         //what port to open

const uint8_t bufferlen = 32;                         //total buffer size for the response_data array
char response_data[bufferlen];                        //character array to hold the response data from modules
String inputstring = "";                              //a string to hold incoming data from the PC

// create objects to represent the Modules you're connecting to
// they can accept hardware or software serial ports, and a name of your choice
Ezo_uart Module1(myserial, "DO");
Ezo_uart Module2(myserial, "EC");
Ezo_uart Module3(myserial, "PH");

// the modules are ordered in an array according to their position in the serial port expander
// so Modules[0] holds the module in port1, Modules[1] holds the module in port 2, etc
const uint8_t module_count = 3;                       //total size fo the Modules array
Ezo_uart Modules[module_count] = {                    //create an array to hold all the modules
  Module1, Module2, Module3
};

void setup() {
  Serial.begin(9600);                                 //Set the hardware serial port to 9600
  myserial.begin(9600);                               //set baud rate for the software serial port to 9600
  inputstring.reserve(20);                            //set aside some bytes for receiving data from the PC
  pinMode(s1, OUTPUT);                                //Set the digital pin as output
  pinMode(s2, OUTPUT);                                //Set the digital pin as output
  pinMode(s3, OUTPUT);                                //Set the digital pin as output

  // in order to use multiple circuits more effectively we need to turn off continuous mode and the *ok response
  for (uint8_t i = 0; i < module_count; i++) {        // loop through the modules
    open_port(i + 1);                                 // open the port
    Modules[i].send_cmd_no_resp("c,0");               //send the command to turn off continuous mode
                                                      //in this case we arent concerned about waiting for the reply
    delay(100);
    Modules[i].send_cmd_no_resp("*ok,0");             //send the command to turn off the *ok response
                                                      //in this case we wont get a reply since its been turned off
    delay(100);
    Modules[i].flush_rx_buffer();                     //clear all the characters that we received from the responses of the above commands
  }
}

void loop() {
  if (Serial.available() > 0) {                       //if we get data from the computer
    inputstring = Serial.readStringUntil(13);         //receive it until the carraige return delimiter
    port = parse_input(inputstring);                  //parse the data to either switch ports or send it to the circuit
    open_port(port);                                  //set the port according to the data we sent

    if (inputstring != "") {                          //if we have a command for the modules
      Modules[port - 1].send_cmd(inputstring, response_data, bufferlen); // send it to the module of the port we opened
      Serial.print(port);                             //print the modules port
      Serial.print("-");
      Serial.print(Modules[port - 1].get_name());     //print the modules name
      Serial.print(": ");
      Serial.println(response_data);                  //print the modules response
      response_data[0] = 0;                           //clear the modules response
    }
    else {
      Serial.print("Port is set to ");                //if were not sending a command, print the port
      Serial.println(port);
    }
  }

  for (uint8_t i = 0; i < module_count; i++) {        //loop through the modules and take a reading
    open_port(i + 1);
    print_reading(Modules[i]);
    Serial.print(" ");
  }
  Serial.println();
}

void print_reading(Ezo_uart &Module) {                //takes a reference to a Module
  //send_read() sends the read command to the module then converts the 
  //answer to a float which can be retrieved with get_reading()
  //it returns a bool indicating if the reading was obtained successfully
  if (Module.send_read()) {                           
    Serial.print(Module.get_name());                  //prints the module's name
    Serial.print(": ");
    Serial.print(Module.get_reading());               //prints the reading we obtained
    Serial.print("    ");
  }
}


uint8_t parse_input(String &inputstring) {                 //this function will decode the string (example 4:cal,1413)
  int colon = inputstring.indexOf(':');                  //find the location of the colon in the string
  if ( colon > 0) {                                       //if we found a colon
    String port_as_string = inputstring.substring(0, colon);  //extract the port number from the string and store it here
    inputstring = inputstring.substring(colon + 1);    //extract the message from the string and store it here
    return port_as_string.toInt();                     //convert the port number from a string to an int
  }
  else {                                              //if theres no colon
    return port;                                      //return the current port and dont modify the input string
  }
}

void open_port(uint8_t _port) {                                  //this function controls what port is opened on the serial port expander

  if (port < 1 || module_count > 8)_port = 1;                //if the value of the port is within range (1-8) then open that port. If it’s not in range set it to port 1
  uint8_t port_bits = _port - 1;

  digitalWrite(s1, bitRead(port_bits, 0));               //Here we have two commands combined into one.
  digitalWrite(s2, bitRead(port_bits, 1));               //The digitalWrite command sets a pin to 1/0 (high or low)
  digitalWrite(s3, bitRead(port_bits, 2));               //The bitRead command tells us what the bit value is for a specific bit location of a number
  delay(2);                                         //this is needed to make sure the channel switching event has completed
}

Чтобы открыть канал, обозначенный P1-P8 на плате расширителя, отправьте номер канала с двоеточием и команду. Завершите строку возвратом каретки (клавиша ENTER на клавиатуре). Например, 3:i откроет третий канал и запросит информацию об устройстве.

Чтобы открыть канал и не отправлять команду, просто введите номер канала и двоеточие. Завершите строку возвратом каретки (клавиша ENTER на клавиатуре). Например, 2: откроет второй канал. Теперь вы можете отправлять любые команды, специфичные для этого датчика, такие как cal, ? который сообщит информацию о калибровке. Обратитесь к спецификациям датчиков для получения списка команд.

Мы использовали только три из восьми портов. Чтобы использовать больше портов, следуйте схеме подключения, показанной на шаге 1, и перейдите к портам 4, 5 и т.д. При необходимости используйте изоляторы. Пример кода, Serial_port_expander_example, также нуждается в некоторой модификации. Смотрите комментарии в коде для руководства.

Комментарии

Ардуино+