Arduino:Примеры/YunFirstConfig

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Первая настройка Yun[1]

Этот пример показывает, как подключить устройство Yun к WiFi-сети, используя в качестве интерфейса монитор порта в IDE Arduino.

Загрузите скетч на Yun и откройте монитор порта. Он покажет доступные WiFi-сети, а затем спросит, какой из них вы хотите воспользоваться. Вам нужно будет ввести пароль для этой сети, а затем система попросит вас дать название устройству Yun и пароль к нему. Если соединение к выбранной сети будет выполнено успешно, вам придет подтверждающее сообщение, после чего устройству Yun будет присвоен IP-адрес.

Если вставить этот адрес с адресную строку браузера, откроется панель Yun, содержащая дополнительные настройки и опции.

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

Примечание: Для этого скетча требуется, чтобы на устройстве Yun стояла прошивка версии 1.6.2 или новее. Руководство по апгрейду можно прочесть тут, и убедитесь, что у вас стоит самая последняя версия.

Необходимое оборудование

  • Плата или модуль Yun
  • Беспроводная сеть, подключенная к интернету

Цепь

Для этого примера никакой цепи строить не нужно.

Наиболее важные части скетча

Библиотеки

  • Библиотека Process.h, запускающая процессы (и прочие операции вроде shell-скриптов) на процессоре Linux. В данном случае она используется для получения списка точек доступа и выполнения других действий, позволяющих узнать параметры WiFi.

Функции

  • Функция getUserInput(out, obfuscated), берущая данные, которые пользователь вводит через монитор порта, и снова показывающая их в мониторе порта. Переменная obfuscated (тип данных – boolean) используется для того, чтобы при введении пароля печатались звездочки («*»).
  • Функция wifiConfig(yunName, yunPsw, wifissid, wifipsw, wifiAPname, countryCode, encryption), выполняющая при помощи библиотеки Process серию команд и скриптов (под Linux), которые настраивают WiFi и сетевые параметры, выбранные пользователем.
  • Функция startSerialTerminal(), просто инициализирующая два последовательных порта, которые нужны для выполнения действий, требуемых для этого скетча.
  • Функция loopSerialTerminal(), создающая соединение между двумя последовательными портами: один «общается» с PC при помощи USB, а другой – с процессором Linux. У нее есть командный режим, который можно вызвать нажатием на «~». Это выключает Bridge и позволяет выставить разные скорости соединения с чипом Atheros.

Использование

Этот скетч через монитор порта в IDE Arduino запрашивает у пользователя информацию для настройки сетевого соединения. Аппаратный последовательный порт (он находится на 0-ом и 1-ом контактах) уже используется устройством Yun для коммуникации с платой, поэтому данному скетчу для корректной работы потребуется второй последовательный порт. Также стоит подготовить SSID (название точки доступа) и пароль для доступа к WiFi-сети. Загрузите скетч, а затем откройте монитор порта, кликнув на увеличительную лупу в правом верхнем углу IDE Arduino. Вы увидите сообщение – такое же, как на картинке ниже:

Спустя какое-то время на модуле Yun Shield начнет мигать синий светодиод. Когда сканирование завершится, скетч покажет в мониторе порта список доступных точек доступа. Время, необходимое для сканирования, зависит от количества точек доступа и мощности их сигнала. Чтобы выбрать точку доступа, впишите ее номер в поле ввода. Не забудьте выбрать в выпадающем меню, находящемся в правом нижнем углу окна, пункт «NL» (он добавляет символ новой строки).

Выбор точки доступа запускает процесс подключения, после чего Yun «прощупает» сеть на предмет того, открытая она или защищенная. Если сеть защищена, вас попросят ввести пароль (или ключ). Для быстрого и безопасного соединения устройство Yun должно иметь название и быть защищено паролем. После этого ваше устройство готово выключить режим точки доступа и инициировать доступ к выбранной WiFi-сети. В конце этого процесса скетч покажет IP-адрес, полученный при помощи DHCP-сервера.

Если подключение не удастся, скетч сообщит об этом и предложит перезапустить процедуру при помощи сброса Yun.

Код

/*
  Первая настройка Arduino Yún

  При помощи Bridge настраивает WiFi и сетевые параметры на 
  Yún101/YunShield/Yún. Для корректной работы скетча Line Ending нужно 
  выставить на «NewLine». Если у вашей платы два USB-порта,
  используйте штатный USB-порт.

  Цепь:
   Arduino Yún/Yún101/YunShield с прошивкой > 1.6.1

  Создан в марте 2016 года Arduino LLC.

  Этот код не защищен авторским правом.

  http://www.arduino.cc/en/Tutorial/YunFirstConfig
*/

#include <Process.h>

#define MAX_WIFI_LIST 10

String networks[MAX_WIFI_LIST];
String yunName;
String yunPassword;

void setup() {
  SERIAL_PORT_USBVIRTUAL.begin(9600);  // инициализируем последовательную коммуникацию 
  while (!SERIAL_PORT_USBVIRTUAL);     // ничего не делаем, пока не откроется последовательный монитор 

  SERIAL_PORT_USBVIRTUAL.println(F("Hi! Nice to see you!"));  //  "Привет! Рад видеть тебя!"
  SERIAL_PORT_USBVIRTUAL.println(F("I'm your Yun101 assistant sketch"));  //  "Я – скетч-помощник для работы с Yun101"
  SERIAL_PORT_USBVIRTUAL.println(F("I'll help you configuring your Yun in a matter of minutes"));  //  "Я помогу настроить твою Yun за несколько минут"

  SERIAL_PORT_USBVIRTUAL.println(F("Let's start by communicating with the Linux processor"));  //  "Давай начнем с коммуникации с процессором Linux"
  SERIAL_PORT_USBVIRTUAL.println(F("When LED (L13) will light up we'll be ready to go!"));  //  "Если загорелся 13-ый светодиод, значит, все готово!"
  SERIAL_PORT_USBVIRTUAL.println(F("Waiting..."));  //  "Ждем..."
  SERIAL_PORT_USBVIRTUAL.println(F("(in the meanwhile, if you are using the IDE's serial monitor, make sure that it's configured to send a \"Newline\")\n"));  //  "(если используешь монитор порта в IDE Arduino, убедись, что он настроен на отправку символа новой строки (NL)\n"
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  Bridge.begin();  // начинаем контактировать с процессором Linux 
  digitalWrite(13, HIGH);  // когда Bridge будет готова, включаем 13-ый светодиод 

  Process wifiList;
  bool master = false;
  wifiList.runShellCommand(F("iwinfo | grep \"Mode: Master\""));
  while (wifiList.available() > 0) {
    wifiList.read();
    master = true;
  }

  // получаем список доступных сетей:
  wifiList.runShellCommand(F("iwinfo wlan0 scan | grep ESSID | cut -d\"\\\"\" -f2"));

  uint8_t num_networks = 0;
  uint8_t i = 0;
  char c;
  bool dropNet = false;

  networks[0].reserve(32);

  while (wifiList.available() > 0) {
    c = wifiList.read();
    if (c != '\n') {
      networks[i] += c;
    } else {
      // проверяем, нет ли в списке повторяющихся сетей, и если есть, удаляем их: 
      for (uint8_t s = 0; s < i; s++) {
        if (networks[i].equals(networks[s])) {
          dropNet = true;
        }
      }
      if (i <= MAX_WIFI_LIST && dropNet == false) {
        networks[i++].reserve(32);
      } else {
        dropNet = false;
        networks[i]="";
      }
    }
  }

  num_networks = i;

  String encryption;
  String password;
  int chose = 0;

  // если сетей не найдено, запускаем ручную настройку:
  if (num_networks == 0) {
    SERIAL_PORT_USBVIRTUAL.println(F("Oops, it seems that you have no WiFi network available"));  //  "Упс! Похоже, доступных сетей нет"
    SERIAL_PORT_USBVIRTUAL.println(F("Let's configure it manually"));  //  "Значит, запускаем ручную настройку"
    SERIAL_PORT_USBVIRTUAL.println(F("SSID of the network you want to connect to: "));  //  "SSID сети, к которой вы хотите подключиться: "
    networks[0] = getUserInput(networks[0], false);
    SERIAL_PORT_USBVIRTUAL.println(F("Password for the network you want to connect to: "));  //  "Пароль для сети, к которой вы хотите подключиться: "
    password = getUserInput(password, true);
    SERIAL_PORT_USBVIRTUAL.print(F("Encryption (eg WPA, WPA2, WEP): "));  //  "Шифрование (например, WPA, WPA2, WEP): "
    encryption = getUserInput(encryption, false);
  } else {
    // если сети найдены, показываем их, но сначала показываем их количество:
    SERIAL_PORT_USBVIRTUAL.print(F("It looks like you have "));  //  "Итак, мы нашли "
    SERIAL_PORT_USBVIRTUAL.print(num_networks);
    SERIAL_PORT_USBVIRTUAL.println(F(" networks around you "));  //  " сетей вокруг нас"
    SERIAL_PORT_USBVIRTUAL.println(F("Which one do you want to connect to?\n"));  //  "К какой из них вы хотите подключиться?\n"
    for (i = 0; i < num_networks && i < MAX_WIFI_LIST; i++) {
      SERIAL_PORT_USBVIRTUAL.print(i);
      SERIAL_PORT_USBVIRTUAL.println(") " + networks[i]);
    }
    String selection;
    selection = getUserInput(selection, false);
    chose = atoi(selection.c_str());
  }

  // извлекаем информацию о шифровании выбранной сети:
  bool openNet = false;
  wifiList.runShellCommand("iwinfo wlan0 scan | grep \"" + networks[chose] + "\" -A5 | grep Encryption | cut -f2 -d\":\"");
  while (wifiList.available() > 0) {
    c = wifiList.read();
    encryption += c;
  }

  if (encryption.indexOf("none") >= 0) {
    openNet = true;
    encryption = "none";
  }
  if (encryption.indexOf("WPA2") >= 0) {
    encryption = "psk2";
  }
  if (encryption.indexOf("WPA") >= 0) {
    encryption = "psk";
  }
  if (encryption.indexOf("WEP") >= 0) {
    encryption = "wep";
  }

  if (openNet == false && password.length() == 0) {
    SERIAL_PORT_USBVIRTUAL.print(F("It looks like you need a password to connect to "));  //  "Похоже, для подключения нужен пароль: "
    SERIAL_PORT_USBVIRTUAL.println(networks[chose]);
    SERIAL_PORT_USBVIRTUAL.print(F("Write it here: "));  //  "Пишем его здесь: "
    password = getUserInput(password, true);
  }

  // меняем пароль для устройства Yun:
  SERIAL_PORT_USBVIRTUAL.println(F("We are almost done! Give a name and a password to your Yun"));  //  "Почти готово! Впишите название и пароль для Yun"
  SERIAL_PORT_USBVIRTUAL.print(F("Name: "));  //  "Название: "
  yunName = getUserInput(yunName, false);
  SERIAL_PORT_USBVIRTUAL.print(F("Password: "));  //  "Пароль: "
  yunPassword = getUserInput(yunPassword, true);

  // выбираем код страны:
  String countryCode;
  SERIAL_PORT_USBVIRTUAL.println(F("One last question: where do you live?"));  //  "Последний вопрос: где вы живете?"
  SERIAL_PORT_USBVIRTUAL.print(F("Insert a two letters county code (eg IT, US, DE): "));  //  "Впишите две буквы для кода страны (например, RU, BY, UA и т.д.): "
  countryCode = getUserInput(countryCode, false);

  yunName.trim();
  yunPassword.trim();
  networks[chose].trim();
  password.trim();
  countryCode.trim();

  // настраиваем Yun при помощи данных, введенных пользователем:
  wifiConfig(yunName, yunPassword, networks[chose], password, "YUN" + yunName + "AP", countryCode,encryption);

  SERIAL_PORT_USBVIRTUAL.print(F("Waiting for the Yun to connect to the network"));  //  "Ждем, пока Yun подключится к сети"
}

bool Connected = false;
bool serialTerminalMode = false;
int runs = 0;

void loop() {
  if (!serialTerminalMode) {
    String resultStr = "";

    if (!Connected) {
      SERIAL_PORT_USBVIRTUAL.print(".");
      runs++;
    }

    // если подключение занимает более 20 секунд, останавливаем попытки подключения:
    if (runs > 20) {
      SERIAL_PORT_USBVIRTUAL.println("");
      SERIAL_PORT_USBVIRTUAL.println(F("We couldn't connect to the network."));  //  "Подключиться к сети не удалось."
      SERIAL_PORT_USBVIRTUAL.println(F("Restart the board if you want to execute the wizard again"));  //  "Если вы вновь хотите запустить мастер настройки, перезапустите плату"
      resultStr = getUserInput(resultStr, false);
    }

    // проверяем, есть ли IP-адрес:
    Process wifiCheck;
    wifiCheck.runShellCommand(F("/usr/bin/pretty-wifi-info.lua | grep \"IP address\" | cut -f2 -d\":\" | cut -f1 -d\"/\"" ));  // команда, которую вы хотите запустить
    while (wifiCheck.available() > 0) {
      char c = wifiCheck.read();
      resultStr += c;
    }

    delay(1000);

    if (resultStr != "") {
      // IP-адрес получен, значит, останавливаем блок loop(), 
      // показываем значение и запускаем терминал:
      Connected = true;
      resultStr.trim();
      SERIAL_PORT_USBVIRTUAL.println("");
      SERIAL_PORT_USBVIRTUAL.print(F("\nGreat! You can now reach your Yun from a browser typing http://"));  //  "\nОтлично! Теперь вы можете получить доступ к Yun из браузера, вписав туда http://"
      SERIAL_PORT_USBVIRTUAL.println(resultStr);
      SERIAL_PORT_USBVIRTUAL.print(F("Press 'Enter' key twice to start a serial terminal"));  //  "Дважды нажмите Enter, чтобы запустить терминал в мониторе порта"
      resultStr = getUserInput(resultStr, false);
      serialTerminalMode = true;
      //startSerialTerminal();
      SERIAL_PORT_HARDWARE.write((uint8_t *)"\xff\0\0\x05XXXXX\x7f\xf9", 11); // отсылаем команду выключения Bridge
      delay(100);
      SERIAL_PORT_HARDWARE.println("\nreset\n\n");
      SERIAL_PORT_HARDWARE.flush();
      SERIAL_PORT_HARDWARE.println("\nreset\n\n");
      SERIAL_PORT_HARDWARE.write((uint8_t *)"\n", 1);
    }

  } else {
    loopSerialTerminal();
  }
}

String getUserInput(String out, bool obfuscated) {
  /*
    while (SerialUSB.available() <= 0) {}
    while (SerialUSB.available() > 0) {
    char c =  SerialUSB.read();
    out += c;
    }
    return out;
  */
  while (SERIAL_PORT_USBVIRTUAL.available() <= 0) {}
  while (1) {
    char c = SERIAL_PORT_USBVIRTUAL.read();
    if (c == '\n' || c == '\r')
      break;
    else {
      if (c != -1) {
        out += c;
        if (obfuscated)
          SERIAL_PORT_USBVIRTUAL.print("*");
        else
          SERIAL_PORT_USBVIRTUAL.print(c);
      }
    }
  }
  SERIAL_PORT_USBVIRTUAL.println("");
  return out;
}

void wifiConfig(String yunName, String yunPsw, String wifissid, String wifipsw, String wifiAPname, String countryCode, String encryption) {
  Process p;

  p.runShellCommand("blink-start 100"); // запускаем мигание синим светодиодом

  p.runShellCommand("hostname " + yunName); // меняем текущее имя хоста
  p.runShellCommand("uci set system.@system[0].hostname='" + yunName + "'"); // меняем имя хоста в UCI

  p.runShellCommand("uci set arduino.@arduino[0].access_point_wifi_name='" + wifiAPname + "'");

  // этот блок сбрасывает пароль для WiFi:
  p.runShellCommand("uci set wireless.@wifi-iface[0].encryption='" + encryption + "'");
  p.runShellCommand("uci set wireless.@wifi-iface[0].mode='sta'\n");
  p.runShellCommand("uci set wireless.@wifi-iface[0].ssid='" + wifissid + "'");
  p.runShellCommand("uci set wireless.@wifi-iface[0].key='" + wifipsw + "'");
  p.runShellCommand("uci set wireless.radio0.channel='auto'");
  p.runShellCommand("uci set wireless.radio0.country='" + countryCode + "'");
  p.runShellCommand("uci delete network.lan.ipaddr");
  p.runShellCommand("uci delete network.lan.netmask");
  p.runShellCommand("uci set network.lan.proto='dhcp'");

  p.runShellCommand("echo -e \"" + yunPsw + "\n" + yunPsw + "\" | passwd root"); // меняем пароли
  p.runShellCommand("uci commit"); // сохраняем режимы, настроенные через UCI
  p.runShellCommand("blink-stop"); // запускаем мигание синего светодиода

  p.runShellCommand("wifi ");
}

long linuxBaud = 250000;

void startSerialTerminal() {
  SERIAL_PORT_USBVIRTUAL.begin(115200);  // открываем последовательную коммуникацию USB-Serial
  SERIAL_PORT_HARDWARE.begin(linuxBaud); // открываем последовательную коммуникацию к Linux
}

boolean commandMode = false;
void loopSerialTerminal() {
  // копируем из USB-CDC в UART
  int c = SERIAL_PORT_USBVIRTUAL.read();    // считываем из USB-CDC
  if (c != -1) {                            // есть что-то?
    if (commandMode == false) {             // если мы не в командном режиме...
      if (c == '~') {                       // Нажата клавиша '~'?
        commandMode = true;                 // входим в командный режим
      } else {
        SERIAL_PORT_HARDWARE.write(c);      // в противном случае записываем символ в UART
      }
    } else {                                // если мы в командном режиме...
      if (c == '0') {                       // нажата клавиша '0'?
        SERIAL_PORT_HARDWARE.begin(57600);  // задаем скорость на 57600
        SERIAL_PORT_USBVIRTUAL.println("Speed set to 57600");  //  "Выставляем скорость на 57600"
      } else if (c == '1') {                // нажата ли клавиша '1'?
        SERIAL_PORT_HARDWARE.begin(115200); // задаем скорость на 115200
        SERIAL_PORT_USBVIRTUAL.println("Speed set to 115200");  //  "Выставляем скорость на 115200"
      } else if (c == '2') {                // нажата ли клавиша '2'? 
        SERIAL_PORT_HARDWARE.begin(250000); // задаем скорость на 250000
        SERIAL_PORT_USBVIRTUAL.println("Speed set to 250000");  //  "Выставляем скорость на 250000"
      } else if (c == '3') {                // нажата ли клавиша '3'?
        SERIAL_PORT_HARDWARE.begin(500000); // задаем скорость на 500000
        SERIAL_PORT_USBVIRTUAL.println("Speed set to 500000");  //  "Выставляем скорость на 500000"
      } else if (c == '~') {                // нажата ли клавиша '~'?
        SERIAL_PORT_HARDWARE.write((uint8_t *)"\xff\0\0\x05XXXXX\x7f\xf9", 11); // отправляем команду выключения Bridge
        SERIAL_PORT_USBVIRTUAL.println("Sending bridge's shutdown command");  //  "Отправляем команду выключения Bridge"
      } else {                              // нажата ли какая-либо клавиша?
        SERIAL_PORT_HARDWARE.write('~');    // записываем '~' на UART
        SERIAL_PORT_HARDWARE.write(c);      // записываем символ на UART
      }
      commandMode = false;                  // во всех случаях выходим из командного режима 
    }
  }

  // копируем из UART в USB-CDC:
  c = SERIAL_PORT_HARDWARE.read();          // считываем из UART 
  if (c != -1) {                            // получили что-нибудь?
    SERIAL_PORT_USBVIRTUAL.write(c);        // записываем на USB-CDC
  }
}

См.также

Внешние ссылки