Как правильно разделить проект на файлы?
- Войдите на сайт для отправки комментариев
Всем привет!
Начну с того, что раньше я все скетчи писал в "инлайновом" стиле и в итоге некоторые проекты содержали по нескольку сотен строк кода в основном ino-файле. Взявшись за очередной проект я понял, что надо с этим что-то делать. Конечно можно просто вынести всю подноготную в отдельные файлы и положить их рядом с основным и пусть компилятор трудится и все это объединяет, но все-таки хотелось бы сделать это более правильно, например написать класс.
Возьмем конкретный пример: в проекте я хочу использовать модем sim800, его работа - отправить результаты из скетча на сервер. Поскольку это отдельное устройство - сущность, то неплохо было бы всю работу с ним вынести в отдельный класс. Что требуется сделать с модемом: 1)проинициализировать (задать тип подключения, режим, точку подключения и т.п.), 2)собрать пакет для отправки, 3)отправить готовый пакет, 4)считывать ответы
И сразу появляются вопросы:
1. Для работы с модемом я хочу использовать SoftwareSerial (я использую ее в скетче еще для одного устройства), где будет правильней создавать экземпляр SoftwareSerial для sim800? В основном скетче, и затем каким-то образом его передавать в мой класс, либо уже в самом классе, но тогда передавать в конструктор класса пины SOFTWARE_RX и SOFTWARE_TX (на случай если я захочу все параметры держать в основном файле)?
2. Сборка пакета: я хочу собирать два разных пакета данных а)стандартная отправка и б)отправка сообщения об ошибке. В обоих случаях количество GET-параметров, передающихся при запросе разное. Но при этом действия при сборке пакета одни и те же: в начале добавляем адрес, затем перечисляем все переменные GET-запроса и их значения, а в конце добавляем служебную информацию. Как будет правильней: написать два отдельных метода для составления стандартного пакета и пакета об ошибке, либо написать один универсальный, который будет принимать, например два массива (названия переменных и их значения).
3. Чтение ответов модема сейчас выглядит примерно так:
void readSerial() { while (sim800Serial.available()) { Serial.write(sim800Serial.read()); } }
Как его правильней делать? Может его лучше оставить в основном скетче? Либо нужно передать в мой класс объект Serial, который я создаю в основном файле.
В интернетах конечно написано куча всего про ООП, но когда пытаешься все это реализовать в своем конкретном случае - от недостатка опыта и знаний появляются такие глупые вопросы. Прошу опытных товарищей помочь и направить на путь истинный.
Понимание, как распределить код между файлами, приходит с опытом.
Основной критерий - минимизация видимых снаружи элементов в каждом из файлов. Но этого можно добиваться разными путями. А это, опять же, опыт.
Ну на вопрос, "как набраться опыта?" есть две основные рекомендации:
1. Писать больше своего кода.
2. Читать и разбирать больше чужого кода.
Другими словами, "царского пути" здесь нет, единственный путь - уделять программированию больше времени.
Я так думаю, что не открыл для Вас Америку, но в глубине души была надежда на чудо. Но, увы, чудес не бывает.
1. Для классов есть 3 вида наследования : является, содержит, использует. В Вашем случае совершенно точно на первый, скорее всего, не второй, значит третий. Создаете объект для собственно обмена и передаете его для использования вашему классу.
2. Многие делают универсальный метод, а потом прячут его за несколькими усеченными методами с подстановкой заглушек вместо параметров. Возможен вариант с параметрами по умолчанию, но у него есть ограничения. Но, вообще то, перегрузка функций как раз для вашего вопроса и придумана.
3. А как хотите. Если Вы твердо уверены, что дальнейшего наследования от Вашего модема не будет, но "голая" функция ничуть не хуже переопределенного метода.
Писать свою библиотеку для такой монстроузной вещи как sim800, когда ты не знаешь, что с файлами-то делать - очень дурная затея. Возьми готовую, и если интересно, изучи.
Писать свою библиотеку для такой монстроузной вещи как sim800, когда ты не знаешь, что с файлами-то делать - очень дурная затея. Возьми готовую, и если интересно, изучи.
да ладно, первая библиотека, которую я взялся писать - была как раз для GSM модуля, правда не СИМ800, а Atinker A6. Но библиотеку писал как раз на основе либы от СИМ.
Библиотеку я, конечно, в итоге так и не написал - упорства и знаний не хватило, но в ходе этой попытки научился очень многому.
ТС у тебя в башке каша, причем с комочками, сырая и недопересоленная. Сначала разберись с файлами проекта, они вообще к ООП ни коим боком, это совсем разные вещи. Есть разбиение проекта на файлы и без ООП, и очень много такого есть, есть ООП без отдельного файла. Это совсем разное, не связанное. А лет через 5-10 может и про библиотеки можно задуматься будет. Не спеши с этим, хорошая либа должна уметь работать в разных проектах без внесения изменений в нее и не мешать остальному. Это сложно. Потому хорошая либа - редкость большая.
Проект делят на файлы по соображениям минимума связей между файлами и логической завершенности каждого файла. Как правило оба критерия совпадают, хотя есть и исключения.
SoftwareSerial - яд. Он не работает почти что вообще. Любой проект где оно как бы работает со слов автора - недотестированный, глючит хоть изредка, но зато обязательно.
Про "нескольку сотен строк кода" - спасибо, поржал. А несколько тысячь строк не хош?! В небольшом проекте с пяток таких файлов. Не парся по пустякам.
Писать библиотеку для этого дела я вообще не собирался, не дорос я еще до написания библиотек. Я просто хочу удобства в написании и редактировании скетча. Меня немного напрягает, когда делаешь правку в одном куске кода, а потом крутишь колесом мыши, чтобы сделать правку в другом куске, связанном с этим. Причем я понимаю, что код еще будет разрастаться. А еще меня напрягает дублирование кода, пусть с небольшими изменениями, но все-таки дублирование.
Попробую сначала написать обособленные функции и распихать их в файлы, лежащие в папке скетча.
SoftwareSerial - яд. Он не работает почти что вообще. Любой проект где оно как бы работает со слов автора - недотестированный, глючит хоть изредка, но зато обязательно.
Подскажите тогда как быть, если к одной ардуине надо подключить по UART несколько устройств (у меня это модем и RS485 - адаптер)? Что использовать вместо SoftwareSerial? Про Мегу с ее четырьмя последовательными портами я знаю, но мне кажется избыточным использовать такую махину с 50+ цифровыми пинами из которых будут задействованы только 5-6 штук.
Блюпил.
я использую SomeSerial но она только для некоторых частот кварца, 16 мегагерц - есть
SoftSerial нормально работает если один экземпляр запущен. Подглючивают только если их несколько одновременно забубенить. У меня уже больше года Nano<--SS-->ESP8266 очень плотно коммуницируют. Никаких глюков за все время. Ну да, пришлось простенький контроль целостности данных для надежности допилить, но это и с железным USART не помешает
//Подскажите тогда как быть
Наилучший вариант - таки выбирать контроллер с нужным набором переферии. Два uart совсем не экзотика. Например atmega128, поищите по форуму, тут были дельные советы по ещё применению. Ещё клон 328p новый китайский с двумя uart вроде есть.
Вариант посложней - добавляем контроллер uart подключаемый по i2c или spi. Такие есть готовые но несложно и вторую 328p на эту роль подрядить.
Ещё вариант. Посмотреть и попробовать другие библиотеки программно реализующие uart. Они все хвалятся что лучше чем softwaresirial, это в принципе реально. Критерий отбора - при закоротке rx и tx идёт стабильный приём того что отправляется. Т.е. данные пробегают по кругу и возвращаются без потерь. Если ещё на это не влияет аппаратный сириал - годно. но скорость будет низкая.
Еще вариант. Присмотреться к своей системе. Если там обычный rs485 на одной витой паре, то критерий из предыдущего абзаца тут не актуален, приём и передача одновременно не актуальны, можно softwaresirial применит для 485, а аппаратный на модем. Этот подход ещё и хорош для определения незанятости канала 485го. Но либу скорей всего придётся править. И быть готовым к периодическим глюкам.
SoftSerial нормально работает если один экземпляр запущен. Подглючивают только если их несколько одновременно забубенить. У меня уже больше года Nano<--SS-->ESP8266 очень плотно коммуницируют. Никаких глюков за все время. Ну да, пришлось простенький контроль целостности данных для надежности допилить, но это и с железным USART не помешает
Так esp - это не модем. Да и протокол свой с контролем целостности - не стандартный АТ. Если с esp по схеме запрос - ответ, то вполне. А модем может в любой момент времени известить нас о своем событии: входящие звонок, сообщение, сеть лягла и т.д. И если в этот же момент МК вздумает отправить команду то возникает двусторонний обмен. А SS его не может, сообщения бются контроля целостности нет. Отсюда пропуски звонков, СМС и пр. глюки. Из вышесказанного понятно что проблема возникает не часто и зависит от кучи подробностей, но она похоже не устранима.
Ещё на качество приёма влияет загруженность остальных прерываний, с такими же последствиями в целом.
У меня запросы разделены по времени: сначала делаю запрос по rs485, жду ответа, затем из ответа достаю нужные данные и отправляю их на сервер через модем и жду ответа от сервера. Прием звонков не планируется. Максимум что будет - прием СМС с настройками GPRS соединения (точка доступа, логин, пароль) - но это однократно за долгий период работы. Ну и СМС не обязательно сразу же принимать, можно в свободное от работы время.
Меня немного напрягает, когда делаешь правку в одном куске кода, а потом крутишь колесом мыши, чтобы сделать правку в другом куске, связанном с этим.
Это индусская архитектура. Используй парадигму: "Одна сущность - одна единица трансляции(модуль, файл)".
Наилучший вариант - таки выбирать контроллер с нужным набором переферии.
хотите 6 уартов, или 6 i2c, или 6 SPI? Или любую комбинацию из перечисленного? - берите SAMD21 !
Но, возможный вариант событий таков: вы его возьмете, протрахаетесь дней 5-6, обложите всеми болтами ардуино, напишите свой вариант-файл, и дальше, в зависимости от настойчивости и опыта, либо добьетесь чего хотели, либо забьете и возьмете Мегу(а лучше Дуе), ну либо протрахаетесь еще недельку.
Моя мысля об том, что брать "контроллер с нужным набором переферии" - это конечно правильно, но при условии, что ты с умеешь управляться с этими контроллерами легко и непринужденно. А это, как ни крути, знания и опыт, которые стоит приобретать заранее, а не когда петух клюнул. Поэтому, если есть возможность взять чего-то покруче чем атмега328 (к примеру) , стоит брать чтоб расширить свой набор способов решения задач.
Моя мысля об том, что брать "контроллер с нужным набором переферии" - это конечно правильно, но при условии, что ты с умеешь управляться с этими контроллерами легко и непринужденно. А это, как ни крути, знания и опыт, которые стоит приобретать заранее, а не когда петух клюнул. Поэтому, если есть возможность взять чего-то покруче чем атмега328 (к примеру) , стоит брать чтоб расширить свой набор способов решения задач.
Поддерживаю эту точек зрения. Конечно хорошо бы взять какой-нибудь контроллер покруче, но нужно какое-то время, чтобы к нему привыкнуть, изучить нюансы работы с ним. Например заливка скетча, библиотеки, толерантность к 5 вольтам и т.п. И лучше бы начинать знакомство с проекта попроще.
Подскажите тогда как быть, если к одной ардуине надо подключить по UART несколько устройств (у меня это модем и RS485 - адаптер)? Что использовать вместо SoftwareSerial? Про Мегу с ее четырьмя последовательными портами я знаю, но мне кажется избыточным использовать такую махину с 50+ цифровыми пинами из которых будут задействованы только 5-6 штук.
Использовать HardwareSerial.
Вот представьте, Вам нужно упаковать груз длиной 2.2 м, а в наличии только ящики по 2 м и 5 м.
5 м, конечно, избыточно, но в 2 м ящики груз не помещается.
Есть, правда, еще вариант ящиков на 2.5 м, но раньше Вы с такими никогда не работали.
В общем, вариантов, как всегда, больше одного, но при этом 2 м - совсем не вариант.
Если продолжать аналогию с ящиками, то софтовый сериал - это положить груз в ящик 2м, а оставшиеся 20 см. обмотать скотчем и синей изолентой :-)
Подскажите тогда как быть, если к одной ардуине надо подключить по UART несколько устройств (у меня это модем и RS485 - адаптер)? Что использовать вместо SoftwareSerial? Про Мегу с ее четырьмя последовательными портами я знаю, но мне кажется избыточным использовать такую махину с 50+ цифровыми пинами из которых будут задействованы только 5-6 штук.
Использовать HardwareSerial.
HardwareSerial, как наиболее загруженный. А программный для вспомогательных целей (настройка, отладка).
Была задачка с 3-мя UART-ами + пассивный бипер. Едва выкрутился на обычной Ардуино.)
Моя мысля об том, что брать "контроллер с нужным набором переферии" - это конечно правильно, но при условии, что ты с умеешь управляться с этими контроллерами легко и непринужденно. А это, как ни крути, знания и опыт, которые стоит приобретать заранее, а не когда петух клюнул. Поэтому, если есть возможность взять чего-то покруче чем атмега328 (к примеру) , стоит брать чтоб расширить свой набор способов решения задач.
Та ниче тут крамольного. Тыж заметь, я ему что советовал 128-й и клон 328-года. Не stm там всякие, а максимально близкое к родному. Зоопарк заводить - та ну его в пень. Сам вполне обхожусь линейкой 328й, esp8266 , OrangePi. Плюс клон от вавгета. Но если бы припекло то что-то близкое к каждому из перечисленных взял бы.
Основной файл:
0. краткое описание проекта (по существу манифест)
1. подключение библиотек
2. создание глобальных переменных, в том числе и переменных собственных стилей
3. инициализация setup
4. основной цикл loop
примерно так:
Подключаемые файлы .h если это объектная модель должны в себе и описание данных и описание функций работы с ними, в них нужно минимизировать использовать переменных и классов из соседних файлов (включая глобальные), если требуется использование глобального объекта то используем параметр с указателем на него а не его самого.
То есть стремится нужно к тому, что 1 файл это что-то одно очень конкретное и цельное, ну например ты вынес все функции работы со временем в отдельный файл, вроде удобно, а тут тебе в другом файле понадобилось время, тут дилема или копировать код или вызывать функции из другого модуля.
Оба эти подхода не верные, правильным будет сделать отдельно библиотеку абстрактных классов (что такое абстрактный класс почитай в инете) и этот указатель на потомок на этот класс передавать туда где он нужен, а там уже в зависимости от условий работать с той веткой которую мы получили, в моем примере так сделано например описание переменной DT, она может быть нескольких разных классов имеющих одного родителя.
Смысл такого подхода в том, что так легко реализуются переопределение как данных так и процедур работы с ними без изменения кода в третьих модулях.
пока писал понял, что написал наверно не очень понятно :) но переписывать не буду
Я вот ахреневаю от того, что ООП тащат в маленькие микроконтроллеры. Как то раньше обходились. Вот на днях супер картинку приводили про сравнение АВР и процессора из космоса, считающего траектории. Как то всё было в пользу АВР, кроме программиста. Нет я не против. Нравиться пожалуйста. Только вот МК имеет одну программу. Ни каких ОС. Ограничения по памяти. И существенный порог по образованию при переходе на ООП. Одно дело на ББ. Там без ООП совсем грустно. На лицо существенное ускорение процесса программирования. Но для МК ни разу не потребовалось ООП.
Только большинство проектов не подразумевает развития и добавления функционала. Сделал,проверил,установил,забыл. Соответственно и ООП там нафиг не нужен.
не совсем так... сделал станок ЧПУ и потом еще года 3 его модифицируешь, то свет добавишь, то алгоритмы улучшишь, то концевик заменишь вместо кнопки поставишь сверх точный цифровой.
Да в любом проекте так, сделал и забыл это скорее исключение...
На Ардуино обычно проекты относительно мелкие и поддерживать их не составляет большого труда. Из своей практики, за последние 6 лет ко мне обращалось буквально пару заказчиков для изменения кода. Думаю, многие просто осваивают сами.
То есть стремится нужно к тому, что 1 файл это что-то одно очень конкретное и цельное, ну например ты вынес все функции работы со временем в отдельный файл, вроде удобно, а тут тебе в другом файле понадобилось время, тут дилема или копировать код или вызывать функции из другого модуля.
Оба эти подхода не верные, правильным будет сделать отдельно библиотеку абстрактных классов
поясните, почему "вызывать функции из другого модуля" - неверный подход? Чем это отличается от использования библиотек?
Вот, например, у меня в одном из проектов может на выбор использоваться связь по NRF, CAN или RS485. Чтобы не усложнять код, всю работу с конкретными протоколами я вынес в отдельные файлы, создав для этих библиотек однотипный интерфейс. условно с двумя функциями my_Send() my_Receive().
Теперь все, что мне нужно, чтобы перенастроить работу с CAN на NRF24 - это поменять include "my_Can.h" на include "my_NRF.h". И чем это плохо?
Я понимаю, что можно это написать и через абстрактный класс. Только зачем?
я писал плохо делать так:
основная программа - О
Модуль - 1
Модуль - 2
Так вот плохо из модуля-1 вызывать процедуры которые находятся в модуле-2 напрямую, правильно делать так
В основном модуле создаем обьект который описан в модуле-2 а в модуле-1 использовать этот объект созданный в основном модуле... В этом случае интерфейс отделяется от фактической реализации...
В основном модуле создаем обьект который описан в модуле-2 а в модуле-1 использовать этот объект созданный в основном модуле... В этом случае интерфейс отделяется от фактической реализации...
использовать в модулях обьекты. созданные в основной программе - ровно так же плохо, как и в других модулях. Правильное разбиение программы, это когда в модуле-1 используются только обьекты и методы модуля-1, а в модуле-2 - соответственно только его .
А основная программа вызывает процедуры из обоих модулей. Именно о таком разбиении я и писал в своем примере.
в модуле используется ссылка(указатель) на объект, например так сделана библиотека датчиков далласа, там в нее передается экземпляр объекта OneWire который инициализируется в основном модуле.
То есть надо учитывать совместное использование. Вот у меня например есть объект который работает с датами, от него наследник который работает с датами и заодно с платой часов реального времени а другой объект будет синхронизироватся с интернетом, в результате имеем 3 разных варианта объекта, и используем их в 7 разных модулях.
/*Я просто хочу удобства в написании и редактировании скетча. Меня немного напрягает, когда делаешь правку в одном куске кода, а потом крутишь колесом мыши, чтобы сделать правку в другом куске, связанном с этим. Причем я понимаю, что код еще будет разрастаться. А еще меня напрягает дублирование кода, пусть с небольшими изменениями, но все-таки дублирование.
*/
Для удобстватредактирования можно использовть внешний редактор notepad++. Я так делаю
Для удобстватредактирования можно использовть внешний редактор notepad++. Я так делаю
У Visual Studio и у Atmel Studio есть платный ардуиновский плагин. Стоит 12 баксов в год. Пользую, удобно, рекомендую.
Для удобстватредактирования можно использовть внешний редактор notepad++. Я так делаю
У Visual Studio и у Atmel Studio есть платный ардуиновский плагин. Стоит 12 баксов в год. Пользую, удобно, рекомендую.
Это не для меня, я комп дома редко включаю, а на работе кроме блокнота админы запрещают ставить другие проги. Вот на работе пишу в ноутпаде, дома редактирую в нем и шью через IDE. Знаю, есть куча других удобных способов, но пока не до них. В ноутпаде тоже удобно.
Гораздо важнее для меня правильно структурировать программу, чтобы если что-то поменять в функциональности, не приходилось весь код заново отлаживать. Поэтому в больших проектах без ООП делать нечего я считаю.
У Visual Studio ..... есть платный ардуиновский плагин.
Именно у VS? Не у VSC? Оно умеет с есп работать? СДК еспшная нормально подхватывается? Я чет с бесплатным VSCшным плагином так и не нашел общего языка.
Именно у VS? Не у VSC? Оно умеет с есп работать? СДК еспшная нормально подхватывается?
У Visual Studio
Подхватывает все установленные в Ардуино ИДе библиотеки и cores для других МК. То есть умеет работать со всем, с чем умеет оригинальная Ардуино ИДЕ.
Подхватывает все установленные в Ардуино ИДе библиотеки и cores для других МК. То есть умеет работать со всем, с чем умеет оригинальная Ардуино ИДЕ.
Это по крайней мере звучит - супер. Если не сложно, можно ссылочку на плагин?
Это по крайней мере звучит - супер. Если не сложно, можно ссылочку на плагин?
https://www.visualmicro.com/
В "Отвлеченных" есть тема, посвященная VMicro. Там, кстати, народ искал с кем бы купить лицензию "на троих"
на пятерых лучше