Сюда буду выкладывать приложения под 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-й системе счисления двумя символами. Внешний вид: Исходники: 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); } } } т.е. несложные практические задачи можно решать за вечер даже сильно не разбираясь в программировании Краткая видео-демонстрация работы:
С наступающим 2018 всех - сделал программу для управления робо-рукой на 4-х сервоприводах. Теперь можно управлять манипулятором прямо с компьютера и нарезать задания которые можно сохранять в файл не переписывая код в Aрдуино. Интерфейс программы: Код для 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
Программирование это та область где нужно учиться добывать знания самостоятельно. Проблема в том что решение любой конкретной прикладной задачи возможно огромным количеством способов, поэтому я не рекомендовал бы изучать программирование по примерам выложенным в Интернет (образцы кода брать можно – но в любом случае нужно четко понимать как они работают и не факт что найденное готовое решение будет оптимально именно для решения Вашей задачи). Я обычно перед изучением нового языка покупаю толстенькую книжку типа вот этой – и тупо решаю из них все примеры которые могут потребоваться для реализации каких-либо задач. (потом уже можно в нагрузку купить один толстый справочник по языку и заглядывать туда, но на первое время если у Вас нет опыта он Вам будет бесполезен) Например в последней программе потребовалось сохранять данные в файл. В книге этому посвящена отдельная глава (48 страниц) – т.е. описано много различных способов. Я ознакомился со всеми и выбрал тот, который показался на мой взгляд наиболее удобным и оптимальным. Даже если я для Вас разжую именно этот один конкретный способ - при решении Вашей задачи он Вам может просто тупо не подойти. Книги наше все – никакие Ютубы, форумы и тематические сайты Вам их не заменят если вы преследуете цели больше чем просто мигать светодиодом….
У Вас есть исходный код который шел в прошивке Arduino Uno с завода для данной руки. Игрался разными прошивками и не могу вернуть как было. На сайте производителя код выдает ошибку компиляции на этапе проверки. http://cdn.sinoning.com/code/snarm.zip Заранее спасибо.
Вопрос выше отпал. Получил код от производителя. Хотел уточнить каким образом Вы калибровали сервопривода мри монтаже? Какие начальные координаты? Т.е. при сборке первую шестерню можно вставить под любым углом поворота по отношению к сервоприводу. От этого зависит в последующем координаты положения рычага руки и заданные данные в программе по углу поворота сервопривода.
Спасибо за примеры, а где скорость порта указана в исходнике? Можете примером поделится как отобразить значения с arduino в окне?
всем привет, прикольно конечно с компа мышкой и клавой рулить железками в реальном времени, а можно так открывать ком порт чтоб микроконтроллер не перезапускал прошивку? а то в некоторых приложениях это наверно будет нежелательно а то и вовсе не допустимо?
Почему нет то, Деда? Перерезать дорожку от DTR к Reset'у, точнее перемычкой заменить, и ставить только на время прошивки.
На виндовсе решал задачку немного похожую (давно правда было, в прошлой жизни, сейчас стараюсь не использовать ОС которой непонятно как управлять). Пользователь запускал приложение, которое работало с последовательным портом, а потом не выгружая приложения выполнялась смена пользователя (смена учетной записи). Другой пользователь тоже запускал приложение и получал ошибку (потому как порт занят), заказчику это очень не нравилось. Я предложил заставить пользователя завершать приложение перед сменой учетной записи, получил ответ: никто завершать не будет, пользователь уходя с рабочего места заблокировал экран и ушел, а другой пользователь под своей учетной записью выполнил вход и должен иметь возможность работать в приложении с использованием последовательного порта. Решение простое, сделал шлюз из ip в последовательный порт, а приложение минимально переделал для работы через этот шлюз. Поскольку железо подключалось к usb, то найти в девайсах нужный порт с именем содержащим CH340 или CP210x и т.п. не составило труда, подключили устройство, определили соответствующий порт, открыли его и больше не закрываем.
Доброго дня!!! Очень вдохновилась вашей идеей, но для моего проекта нужна ещё одна серва. Можете подсказать существует ли способ добавить её используя данную кодировку? Добавила скеч в ардуинку и программу поменяла, но при перетаскивание ползунка общения с ардуинкой через порт не происходит.