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
    shpock, БАРМАТОГРАФ, IvanUA и 2 другим нравится это.
  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 нравится это.
  8. Tolik

    Tolik Нуб

    У Вас есть исходный код который шел в прошивке Arduino Uno с завода для данной руки.
    Игрался разными прошивками и не могу вернуть как было.

    На сайте производителя код выдает ошибку компиляции на этапе проверки.
    http://cdn.sinoning.com/code/snarm.zip

    Заранее спасибо.
     

    Вложения:

    • Ошибка.jpg
      Ошибка.jpg
      Размер файла:
      178,5 КБ
      Просмотров:
      34
    • me_arm3.0.ino
      Размер файла:
      2,3 КБ
      Просмотров:
      30
  9. Tolik

    Tolik Нуб

    Вопрос выше отпал.
    Получил код от производителя.

    Хотел уточнить каким образом Вы калибровали сервопривода мри монтаже? Какие начальные координаты?
    Т.е. при сборке первую шестерню можно вставить под любым углом поворота по отношению к сервоприводу.
    От этого зависит в последующем координаты положения рычага руки и заданные данные в программе по углу поворота сервопривода.