C# приложения для управления Arduino из под Windows

Тема в разделе "Глядите, что я сделал", создана пользователем issaom, 7 дек 2017.

  1. issaom

    issaom Гик

    Сюда буду выкладывать приложения под Windows для управления Arduino. Может кому пригодятся куски кода или все приложение )))
    Первый проект - Windows Servo Tester. Разработано на C# в бесплатной версии Visual Studio community от мелкософта.
    Для передачи данных используется самопальный протокол на ASCII который подойдет для очень многих проектов ;-)

    Код (C++):
    const char StaPack = '#';              // Признак начала пакета данных
    const char EndPack = '|';                                  // Признак окончания пакета данных
    const uint16_t TimeOut = 500;                       // Отвалились через это время mls

    #include <Servo.h>      //используем библиотеку для работы с сервоприводом
    Servo servo;                  //объявляем переменную servo типа Servo

    void setup() {
      Serial.begin(9600);
      pinMode(13, OUTPUT);
      servo.attach(10);     //сервопривод на порту 10
      servo.write(0);
    }

    byte hexToByte (String StrControlHex) {         //0 : 255 (1 байт)

      uint8_t  HEX16 = 0;   // число 16-е из символа
      uint8_t  exp16 = 1;   // степень числа 16
      uint8_t  decBy = 0;   // число 10-е расчитанное без знака

      StrControlHex.remove(0, 2);           //отрезаем управляющие символы (2шт)
      int i = StrControlHex.indexOf('|');   //определяем дилнну строки
      if (i == -1) return 0;                          //фигня пришла а не байт
      if (i > 2)   return 0;                             //фигня пришла а не байт
      StrControlHex.remove(i, 1);           //отрезаем управляющие символы ('|')

      for (int j = StrControlHex.length() - 1; j >= 0; j--) {

        HEX16 = StrControlHex.charAt(j);

        if (HEX16 >= 48 && HEX16 <= 57) HEX16 = map(HEX16, 48, 57, 0, 9);
        if (HEX16 >= 65 && HEX16 <= 70) HEX16 = map(HEX16, 65, 70, 10, 15);
        if (HEX16 >= 97 && HEX16 <= 102) HEX16 = map(HEX16, 97, 102, 10, 15);

        decBy = decBy + HEX16 * exp16;
        exp16 = exp16 * 16;

      }
      return decBy;                         //возвращаем десятичное число 1 байт без знака
    }

    void loop() {

    clearPack:                                // переходим сюда если приняли мусор /отвалились по таймауту/

      char   IncomChar;
      String StrControl = "";

      while (Serial.available() > 0) {            // выгребаем все байты которые пришли в буфер Serial

        IncomChar = Serial.read();

        if (IncomChar == StaPack) {           // пришел символ начала посылки данных
          StrControl += IncomChar;             // плюсуем пришедший символ к строке
          unsigned long currentTime = millis(); // записываем текущее время

          ReceptionPacket:                    // начало приема пакета

          if (Serial.available() > 0) {
            IncomChar = Serial.read();
            StrControl += IncomChar;          // плюсуем пришедший символ к строке
            if (IncomChar == EndPack) break;  // вываливаемся из цикла выгребания байт если приняли весь пакет
          }

          if (millis() - currentTime >= TimeOut) goto clearPack;  // отвалились по TimeOut

          goto ReceptionPacket;               // переход в начало приема пакета

        }

      }                                       // конец цикла по выгребанию байт

      if (StrControl != "") {

      //Serial.println(StrControl);           //печатаем принятый пакет отладка

      switch (StrControl.charAt(1)) {       //применяем полученую строку
        case 'w':
          digitalWrite(13, 0);
          break;
        case 'x':
          digitalWrite(13, 1);
          break;
        case 'A':
          servo.write(hexToByte(StrControl));
          break;
      }
      }
    }
    Примеры команд (можно и из терминала посылать)
    #x| - включить светодиод на 13м пине
    #w|- выключить светодиод на 13м пине
    #Af|- повернуть сервопривод на 15 градусов
    #A5а|- повернуть сервопривод на 90 градусов

    Байт передается в 16-й системе счисления двумя символами.

    Внешний вид:

    upload_2017-12-7_1-12-51.png

    Исходники:
    https://yadi.sk/d/Zsp8FM1t3QPE3T

    Код главной формы в C#

    Код (C++):
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.IO.Ports;

    namespace WindowsServoTester
    {
        public partial class Form1 : Form
        {
            bool isConnected = false;
            SerialPort port;
            public Form1()
            {
                InitializeComponent();
            }

            private void button1_Click(object sender, EventArgs e)
            {
                comboBox1.Items.Clear();
                // Получаем список COM портов доступных в системе
                string[] portnames = SerialPort.GetPortNames();
                // Проверяем есть ли доступные
                if (portnames.Length == 0)
                {
                    MessageBox.Show("COM PORT not found");
                }
                foreach (string s in portnames)
                {
                    //добавляем доступные COM порты в список          
                    comboBox1.Items.Add(s);
                }
            }

            private void connectToArduino()
            {
                isConnected = true;
                string selectedPort = comboBox1.GetItemText(comboBox1.SelectedItem);
                port = new SerialPort(selectedPort, 9600, Parity.None, 8, StopBits.One);
                port.Open();
                button2.Text = "Disconnect";
            }

            private void disconnectFromArduino()
            {
                isConnected = false;
                port.Close();
                button2.Text = "Connect";
            }

            private void button2_Click(object sender, EventArgs e)
            {
                if (!isConnected)
                {
                    connectToArduino();
                }
                else
                {
                    disconnectFromArduino();
                }
            }

            private void button3_Click(object sender, EventArgs e)
            {
                port.Write("#x|");
            }

            private void button4_Click(object sender, EventArgs e)
            {
                port.Write("#w|");
            }

            private void trackBar1_Scroll(object sender, EventArgs e)
            {
                String UgolServo = "#A";
                UgolServo = UgolServo + Convert.ToString(trackBar1.Value, 16);
                UgolServo = UgolServo + "|";
                port.Write(UgolServo);
                label1.Text = String.Format("Угол сервопривода: {0}", trackBar1.Value);
            }
        }
    }
     
    т.е. несложные практические задачи можно решать за вечер даже сильно не разбираясь в программировании

    Краткая видео-демонстрация работы:

     
    Последнее редактирование: 7 дек 2017
  2. Tomasina

    Tomasina Иномирянин

    :mad:
     
  3. Tomasina

    Tomasina Иномирянин

    Я имею в виду построение фразы.
     
    Толик Иванов нравится это.
  4. REMBOV

    REMBOV Воскреситель Матрёшек

    научите?
     
  5. issaom

    issaom Гик

    С наступающим 2018 всех - сделал программу для управления робо-рукой на 4-х сервоприводах. Теперь можно управлять манипулятором прямо с компьютера и нарезать задания которые можно сохранять в файл не переписывая код в Aрдуино.



    Интерфейс программы:

    Windows robot arm.JPG

    Код для Arduino:

    Код (C++):
    const char StaPack = '#';                // Признак начала пакета данных
    const char EndPack = '|';                // Признак окончания пакета данных
    const uint16_t TimeOut = 500;            // Отвалились через это время mls

    #include <Servo.h>      //используем библиотеку для работы с сервоприводом
    Servo servo1;           //объявляем переменную servo типа Servo
    Servo servo2;           //объявляем переменную servo типа Servo
    Servo servo3;           //объявляем переменную servo типа Servo
    Servo servo4;           //объявляем переменную servo типа Servo

    void setup() {
     
    Serial.begin(9600);
    pinMode(4, OUTPUT);   //led
    //pinMode(5, OUTPUT); //na
    servo1.attach(11); // grapple
    servo2.attach(10); // up-down
    servo3.attach(9);  // forward backward
    servo4.attach(6);  // rotation

    servo1.write(35);
    servo2.write(75);
    servo3.write(30);
    servo4.write(90);

    }

    byte hexToByte (String StrControlHex) {         //0 : 255 (1 байт)

      uint8_t  HEX16 = 0;   // число 16-е из символа
      uint8_t  exp16 = 1;   // степень числа 16
      uint8_t  decBy = 0;   // число 10-е расчитанное без знака

      StrControlHex.remove(0, 2);           //отрезаем управляющие символы (2шт)
      int i = StrControlHex.indexOf('|');   //определяем дилнну строки
      if (i == -1) return 0;                //фигня пришла а не байт
      if (i > 2)   return 0;                //фигня пришла а не байт
      StrControlHex.remove(i, 1);           //отрезаем управляющие символы ('|')

      for (int j = StrControlHex.length() - 1; j >= 0; j--) {

        HEX16 = StrControlHex.charAt(j);

        if (HEX16 >= 48 && HEX16 <= 57) HEX16 = map(HEX16, 48, 57, 0, 9);
        if (HEX16 >= 65 && HEX16 <= 70) HEX16 = map(HEX16, 65, 70, 10, 15);
        if (HEX16 >= 97 && HEX16 <= 102) HEX16 = map(HEX16, 97, 102, 10, 15);

        decBy = decBy + HEX16 * exp16;
        exp16 = exp16 * 16;

      }
      return decBy;                         //возвращаем десятичное число 1 байт без знака
    }


    int16_t hexToInt16 (String StrControlHex) {      // -32768 : 32767 (2 байта)

      uint8_t  HEX16 = 0;    // число 16-е из символа
      uint16_t exp16 = 1;    // степень числа 16
      int16_t  dec16 = 0;    // число 10-е расчитанное со знаком

      StrControlHex.remove(0, 2);           //отрезаем управляющие символы (2шт)
      int i = StrControlHex.indexOf('|');   //определяем дилнну строки
      if (i == -1) return 0;                //фигня пришла а не 2 байта
      if (i > 4)   return 0;                //фигня пришла а не 2 байта
      StrControlHex.remove(i, 1);           //отрезаем управляющие символы ('|')

      for (int j = StrControlHex.length() - 1; j >= 0; j--) {

        HEX16 = StrControlHex.charAt(j);

        if (HEX16 >= 48 && HEX16 <= 57) HEX16 = map(HEX16, 48, 57, 0, 9);
        if (HEX16 >= 65 && HEX16 <= 70) HEX16 = map(HEX16, 65, 70, 10, 15);
        if (HEX16 >= 97 && HEX16 <= 102) HEX16 = map(HEX16, 97, 102, 10, 15);

        dec16 = dec16 + HEX16 * exp16;
        exp16 = exp16 * 16;
      }
      return dec16;                         //возвращаем десятичное число int16_t со знаком
    }


    int32_t hexToInt32 (String StrControlHex) {      // -2147483648 : 2147483647 (4 байта)

      uint8_t  HEX16 = 0;    // число 16-е из символа
      uint32_t exp16 = 1;    // степень числа 16
      int32_t  dec32 = 0;    // число 10-е расчитанное со знаком

      StrControlHex.remove(0, 2);           //отрезаем управляющие символы (2шт)
      int i = StrControlHex.indexOf('|');   //определяем дилнну строки
      if (i == -1) return 0;               //фигня пришла а не 4 байта
      if (i > 8)   return 0;               //фигня пришла а не 4 байта
      StrControlHex.remove(i, 1);          //отрезаем управляющие символы ('|')

      for (int j = StrControlHex.length() - 1; j >= 0; j--) {

        HEX16 = StrControlHex.charAt(j);

        if (HEX16 >= 48 && HEX16 <= 57) HEX16 = map(HEX16, 48, 57, 0, 9);
        if (HEX16 >= 65 && HEX16 <= 70) HEX16 = map(HEX16, 65, 70, 10, 15);
        if (HEX16 >= 97 && HEX16 <= 102) HEX16 = map(HEX16, 97, 102, 10, 15);

        dec32 = dec32 + HEX16 * exp16;
        exp16 = exp16 * 16;
      }
      return dec32;                         //возвращаем десятичное число int32_t со знаком
    }

    void loop() {

    clearPack:                                // переходим сюда если приняли мусор /отвалились по таймауту/

      char   IncomChar;
      String StrControl = "";

      while (Serial.available() > 0) {        // выгребаем все байты которые пришли в буфер Serial

        IncomChar = Serial.read();

        if (IncomChar == StaPack) {             // пришел символ начала посылки данных
          StrControl += IncomChar;              // плюсуем пришедший символ к строке
          unsigned long currentTime = millis(); // записываем текущее время

          ReceptionPacket:                      // начало приема пакета

          if (Serial.available() > 0) {
            IncomChar = Serial.read();
            StrControl += IncomChar;            // плюсуем пришедший символ к строке
            if (IncomChar == EndPack) break;    // вываливаемся из цикла выгребания байт если приняли весь пакет
          }

          if (millis() - currentTime >= TimeOut) goto clearPack;  // отвалились по TimeOut

          goto ReceptionPacket;             // переход в начало приема пакета

        }

      }                                     // конец цикла по выгребанию байт

      if (StrControl != "") {
     
      //Serial.println(StrControl);         // печатаем принятый пакет отладка

      switch (StrControl.charAt(1)) {       // применяем полученую строку
        case 'w':
          digitalWrite(4, 0);
          break;
        case 'x':
          digitalWrite(4, 1);
          break;
        case 'A':
          servo1.write(hexToByte(StrControl));
          break;
         case 'B':
         servo2.write(hexToByte(StrControl));
         break;
         case 'C':
         servo3.write(hexToByte(StrControl));
         break;
         case 'D':
         servo4.write(hexToByte(StrControl));
         break;
      }
      }
    }


     
    Исходники программы C#:

    WindowsControlRobotArmV1.zip
     

    Вложения:

    ИгорьК нравится это.
  6. issaom

    issaom Гик

    Программирование это та область где нужно учиться добывать знания самостоятельно.
    Проблема в том что решение любой конкретной прикладной задачи возможно огромным количеством способов, поэтому я не рекомендовал бы изучать программирование по примерам выложенным в Интернет (образцы кода брать можно – но в любом случае нужно четко понимать как они работают и не факт что найденное готовое решение будет оптимально именно для решения Вашей задачи).
    Я обычно перед изучением нового языка покупаю толстенькую книжку типа вот этой – и тупо решаю из них все примеры которые могут потребоваться для реализации каких-либо задач. (потом уже можно в нагрузку купить один толстый справочник по языку и заглядывать туда, но на первое время если у Вас нет опыта он Вам будет бесполезен)

    изучаемС.jpg

    Например в последней программе потребовалось сохранять данные в файл. В книге этому посвящена отдельная глава (48 страниц) – т.е. описано много различных способов. Я ознакомился со всеми и выбрал тот, который показался на мой взгляд наиболее удобным и оптимальным. Даже если я для Вас разжую именно этот один конкретный способ - при решении Вашей задачи он Вам может просто тупо не подойти. Книги наше все – никакие Ютубы, форумы и тематические сайты Вам их не заменят если вы преследуете цели больше чем просто мигать светодиодом….

    книги.jpg
     
    IvanUA и ИгорьК нравится это.
  7. ИгорьК

    ИгорьК Давно здесь

    Для поколения web 2.0 это открытие.
     
    REMBOV нравится это.