Реклама ⓘ
Главная » Радиоуправление
Призовой фонд
на апрель 2024 г.
1. 100 руб.
От пользователей

Реклама ⓘ

CxemCAR на .NET Micro Framework - Bluetooth управление машинкой с Android

Первая, вводная часть статьи проекта CxemCAR с описанием ПО и исходниками для Android находится здесь. В данной статье я хотел бы описать вариант проекта для контроллеров с .NET Micro Framework. По сравнению с проектом на STM32, изменений мало, за исключением замены контроллера и программной части для него.
 
В качестве контроллера можно использовать Netduino, платы GHI Electronics и др. Я использовал FEZ Panda II:
 
Плата FEZ Panda II
Питание DC двигателей осуществляется от Li-Po аккумуляторов 3.7В 1100 мА. Контроллер питается от отдельного аккумулятора 3.7В (хотя требуется 5В, но прекрасно работает и от 3.7В). Питание Bluetooth модуля берется с платы FEZ.

Фото элементов питания:

Фото элементов питания

Схема подключения выглядит следующим образом:

Схема подключения

Плату FEZ Panda II к 4WD шасси прикрепил при помощи 2-х стороннего скотча:

uprav52-4.jpg

Далее, все было собрано и подключено. Итог на фото:

uprav52-5.jpg

Исходник для .NET Micro Framework:

using System;
using System.IO.Ports;
using System.Threading;
using System.Text;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.FEZ;

namespace CxemCAR
{
    public class Program
    {
        public const char cmdL = 'L';       // команда UART для левого двигателя
        public const char cmdR = 'R';       // команда UART для правого двигателя
        public const char cmdH = 'H';       // команда UART для доп. канала 1 (к примеру сигнал Horn)
        public const char cmdF = 'F';       // команда UART для работы с EEPROM памятью МК для хранения настроек
        public const char cmdr = 'r';       // команда UART для работы с EEPROM памятью МК для хранения настроек (чтение)
        public const char cmdw = 'w';       // команда UART для работы с EEPROM памятью МК для хранения настроек (запись)

        //public const int t_TOut = 2500;     // кол-во миллисекунд, через которое машинка останавливается при потери связи
        static int sw_autoOFF;
        static int autoOFF = 2500;
        static byte[] storage = new byte[InternalFlashStorage.Size];                                // переменная для хранения значений FLASH памяти МК

        static OutputPort MotorL_d = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di4, false);           // направление вращения двигателя 1
        static OutputPort MotorR_d = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di7, false);           // направление вращения двигателя 2
        static OutputPort Channel1 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di8, false);           // доп. канал 1
        static PWM MotorL = new PWM((PWM.Pin)FEZ_Pin.PWM.Di5);                                      // ШИМ вывод для управления двигателем 1 (левый)
        static PWM MotorR = new PWM((PWM.Pin)FEZ_Pin.PWM.Di6);                                      // ШИМ вывод для управления двигателем 2 (правый)
        static SerialPort UART1 = new SerialPort("COM1", 9600);                                     // новый объект UART1 (порт COM1)
        static Timer timerTO;                                                                       // таймер
       
        public static void Main()
        {
            byte[] L_Data = new byte[4];        // строковый массив для данных мотора L
            byte L_index = 0;                   // индекс массива
            byte[] R_Data = new byte[4];        // строковый массив для данных мотора R
            byte R_index = 0;                   // индекс массива
            byte[] H_Data = new byte[1];        // строковый массив для доп. канала
            byte H_index = 0;                   // индекс массива
            byte[] F_Data = new byte[8];        // строковый массив данных для работы с EEPROM
            byte F_index = 0; 
            char command = ' ';                 // команда: передача координат R, L, H, F или конец строки

            int i_tmp_L = 0;
            int i_tmp_R = 0;
            int i_tmp_H = 0;

            byte[] incomingByte = new byte[1];  // байт с UART
          
            UART1.Open();
            UART1.Flush();

            timerTO = new Timer(new TimerCallback(TimeOut), null, autoOFF, autoOFF);  // инициализация таймера потери связи
            timer_init();                                                             // инициализируем программный таймер

            while (true)
            {
                int read_count = UART1.Read(incomingByte, 0, 1);        // считываем данные
                if (read_count > 0)                                     // пришли данные?
                {
                    if (incomingByte[0] == cmdL)                        // если пришли данные для мотора L
                    {
                        command = cmdL;                                 // текущая команда
                        Array.Clear(L_Data, 0, L_Data.Length);          // очистка массива
                        L_index = 0;                                    // сброс индекса массива
                    }
                    else if (incomingByte[0] == cmdR)                   // если пришли данные для мотора R
                    {
                        command = cmdR;                                 // текущая команда
                        Array.Clear(R_Data, 0, R_Data.Length);          // очистка массива
                        R_index = 0;                                    // сброс индекса массива
                    }
                    else if (incomingByte[0] == cmdH)                   // если пришли данные для доп. канала 1
                    {
                        command = cmdH;                                 // текущая команда
                        Array.Clear(H_Data, 0, H_Data.Length);          // очистка массива
                        H_index = 0;                                    // сброс индекса массива
                    }
                    else if (incomingByte[0] == cmdF)                   // если пришли данные для доп. канала 1
                    {
                        command = cmdF;                                 // текущая команда
                        Array.Clear(F_Data, 0, F_Data.Length);          // очистка массива
                        F_index = 0;                                    // сброс индекса массива
                    }
                    else if (incomingByte[0] == '\r') command = 'e';    // конец строки
                    else if (incomingByte[0] == '\t') command = 't';    // конец строки для команд работы с памятью


                    if (command == cmdL && incomingByte[0] != cmdL)
                    {
                        if (ValidData(incomingByte[0]))
                        {
                            L_Data[L_index] = incomingByte[0];              // сохраняем каждый принятый байт в массив
                            if (L_index < (L_Data.Length - 1)) L_index++;   // увеличиваем текущий индекс массива
                        }
                    }
                    else if (command == cmdR && incomingByte[0] != cmdR)
                    {
                        if (ValidData(incomingByte[0]))
                        {
                            R_Data[R_index] = incomingByte[0];
                            if (R_index < (R_Data.Length - 1)) R_index++;
                        }
                    }
                    else if (command == cmdH && incomingByte[0] != cmdH)
                    {
                        if (ValidData(incomingByte[0]))
                        {
                            H_Data[H_index] = incomingByte[0];
                            if (H_index < (H_Data.Length - 1)) H_index++;
                        }
                    }
                    else if (command == cmdF && incomingByte[0] != cmdF)
                    {
                        F_Data[F_index] = incomingByte[0];
                        if (F_index < (F_Data.Length - 1)) F_index++;
                     }
                    else if (command == 'e')                                // если приняли конец строки
                    {
                        timerTO.Dispose();                                  // останавливаем таймер потери связи
                        string tmp_L = new string(System.Text.UTF8Encoding.UTF8.GetChars(L_Data));      // формируем строку из массива
                        string tmp_R = new string(System.Text.UTF8Encoding.UTF8.GetChars(R_Data));
                        string tmp_H = new string(System.Text.UTF8Encoding.UTF8.GetChars(H_Data));

                        try
                        {
                            if (tmp_L != null) i_tmp_L = int.Parse(tmp_L);                              // и пытаемся преобразовать в int
                            if (tmp_R != null) i_tmp_R = int.Parse(tmp_R);
                            if (tmp_H != null) i_tmp_H = int.Parse(tmp_H);
                        }
                        catch { 
                            Debug.Print("Error: convert String to Integer"); 
                        }


                        if (i_tmp_L > 100) i_tmp_L = 100;
                        else if (i_tmp_L < -100) i_tmp_L = -100;
                        if (i_tmp_R > 100) i_tmp_R = 100;
                        else if (i_tmp_R < -100) i_tmp_R = -100;

                        Control4WD(i_tmp_L, i_tmp_R, i_tmp_H);
                        timerTO.Change(autoOFF, autoOFF);                                               // таймер считает сначала
                    }
                    else if (command == 't')                                                            // если приняли конец строки для работы с памятью
                    {
                        Flash_Op(F_Data[0], F_Data[1], F_Data[2], F_Data[3], F_Data[4]);
                    }
                }
            }
        }

        static void Flash_Op(byte FCMD, byte z1, byte z2, byte z3, byte z4)
        {
            if (FCMD == cmdr && sw_autoOFF != 255)                              // если команда чтения EEPROM данных 
            {
                byte[] buffer = Encoding.UTF8.GetBytes("FData:");               // подготавливаем байтовый массив для вывода в UART
                UART1.Write(buffer, 0, buffer.Length);                          // запись данных в UART
                byte[] buffer2 = new byte[4] { storage[0], storage[1], storage[2], storage[3] };
                UART1.Write(buffer2, 0, buffer2.Length);
                byte[] buffer3 = Encoding.UTF8.GetBytes("\r\n");
                UART1.Write(buffer3, 0, buffer3.Length);
            }
            else if (FCMD == cmdw)                                              // если команда записи EEPROM данных
            {
                byte[] varToSave = new byte[InternalFlashStorage.Size];
                varToSave[0] = z1;
                varToSave[1] = z2;
                varToSave[2] = z3;
                varToSave[3] = z4;
                InternalFlashStorage.Write(varToSave);                          // запись данных в FLASH память МК
                timer_init();		                                            // переинициализируем таймер
                byte[] buffer2 = Encoding.UTF8.GetBytes("FWOK\r\n");            // подготавливаем байтовый массив для вывода в UART
                UART1.Write(buffer2, 0, buffer2.Length);	                    // посылаем сообщение, что данные успешно записаны
            }
        }

        static void timer_init()
        {
            InternalFlashStorage.Read(storage);                                 // чтение данных с FLASH памяти
            sw_autoOFF = storage[0];
            if(sw_autoOFF == '1'){                                              // если таймер останова включен
                byte[] var_Data= new byte[3];
                var_Data[0] = storage[1];
                var_Data[1] = storage[2];
                var_Data[2] = storage[3];
                string tmp_autoOFF = new string(System.Text.UTF8Encoding.UTF8.GetChars(var_Data));
                autoOFF = int.Parse(tmp_autoOFF)*100;
                timerTO.Change(autoOFF, autoOFF);                               // изменяем параметры таймера
            }
            else if(sw_autoOFF == '0'){
                timerTO.Dispose();                                              // выключаем таймер
            } 

            Debug.Print("Timer Init" + autoOFF.ToString());
        }

        static void TimeOut(object o)
        {
            //Debug.Print(DateTime.Now.ToString());
            Control4WD(0, 0, 0);                                                // при таймауте останавливаем машинку
        }

        public static void Control4WD(int mLeft, int mRight, int Horn)
        {
            bool directionL, directionR;                                        // направление вращение для L298N
            int valueL, valueR;                                                 // значение ШИМ M1, M2 (0-100)

            if (mLeft > 0)
            {
                valueL = mLeft;
                directionL = false;
            }
            else if (mLeft < 0)
            {
                valueL = 100 - System.Math.Abs(mLeft);
                directionL = true;
            }
            else
            {
                directionL = false;
                valueL = 0;
            }

            if (mRight > 0)
            {
                valueR = mRight;
                directionR = false;
            }
            else if (mRight < 0)
            {
                valueR = 100 - System.Math.Abs(mRight);
                directionR = true;
            }
            else
            {
                directionR = false;
                valueR = 0;
            }

            if (Horn == 1)
            {
                Channel1.Write(true);
            }
            else Channel1.Write(false);

            //Debug.Print("L:" + valueL.ToString() + ", R:" + valueR.ToString());
            
            MotorL.Set(30000, (byte)(valueL));
            MotorR.Set(30000, (byte)(valueR));

            MotorL_d.Write(directionL);
            MotorR_d.Write(directionR);
        }

        public static bool ValidData(byte chIncom)                  // проверка поступившего символа на принадлежность к "0..9" или "-"
        {
            if ((chIncom >= 0x30 && chIncom <= 0x39) || chIncom == 0x2D) return true;
            else return false;
        }
    }
}

Сама программа под FEZ не очень сложная - в цикле считываем данные с UART и формируем соответствующие массивы. Как только получаем символ окончания передачи (\r или \t), то данные преобразовываются и передаются в соответствующие функции Control4WD() или Flash_Op(). В функции Control4WD() происходит вычисление направления, а также небольшие расчеты и затем управляющие сигналы выводятся на соответствующие пины контроллера.

Проект на GitHub

Прикрепленные файлы:

Теги:

Опубликована: 20.02.2013 Изменена: 20.07.2013 0 0
Я собрал 0 1
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 5 Проголосовало: 1 чел.

Комментарии (0) | Я собрал (0) | Подписаться

Статью еще никто не комментировал. Вы можете стать первым.
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется напряжение?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Квадрокоптер Syma X11
Квадрокоптер Syma X11
Сатфайндер Осциллограф DSO138
вверх