Как правильно разделить проект на файлы?

kost82
Offline
Зарегистрирован: 30.11.2015

Всем привет!
Начну с того, что раньше я все скетчи писал в "инлайновом" стиле и в итоге некоторые проекты содержали по нескольку сотен строк кода в основном 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, который я создаю в основном файле.
В интернетах конечно написано куча всего про ООП, но когда пытаешься все это реализовать в своем конкретном случае - от недостатка опыта и знаний появляются такие глупые вопросы. Прошу опытных товарищей помочь и направить на путь истинный. 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Понимание, как распределить код между файлами, приходит с опытом.

Основной критерий - минимизация видимых снаружи элементов в каждом из файлов. Но этого можно добиваться разными путями. А это, опять же, опыт.

Ну на вопрос, "как набраться опыта?" есть две основные рекомендации:

1. Писать больше своего кода.

2. Читать и разбирать больше чужого кода.

Другими словами, "царского пути" здесь нет, единственный путь - уделять программированию больше времени.

 

Я так думаю, что не открыл для Вас Америку, но в глубине души была надежда на чудо. Но, увы, чудес не бывает.

GarryC
Offline
Зарегистрирован: 08.08.2016

1. Для классов есть 3 вида наследования : является, содержит, использует. В Вашем случае совершенно точно на первый, скорее всего, не второй, значит третий. Создаете объект для собственно обмена и передаете его для использования вашему классу.

2. Многие делают универсальный метод, а потом прячут его за несколькими усеченными методами с подстановкой заглушек вместо параметров. Возможен вариант с параметрами по умолчанию, но у него есть ограничения.  Но, вообще то, перегрузка функций как раз для вашего вопроса и придумана.

3. А как хотите. Если Вы твердо уверены, что дальнейшего наследования от Вашего модема не будет, но "голая" функция ничуть не хуже переопределенного метода.

rkit
Offline
Зарегистрирован: 23.11.2016

Писать свою библиотеку для такой монстроузной вещи как sim800, когда ты не знаешь, что с файлами-то делать - очень дурная затея. Возьми готовую, и если интересно, изучи.

b707
Offline
Зарегистрирован: 26.05.2017

rkit пишет:

Писать свою библиотеку для такой монстроузной вещи как sim800, когда ты не знаешь, что с файлами-то делать - очень дурная затея. Возьми готовую, и если интересно, изучи.

да ладно, первая библиотека, которую я взялся писать - была как раз для GSM модуля, правда не СИМ800, а Atinker A6. Но библиотеку писал как раз на основе либы от СИМ.

Библиотеку я, конечно, в итоге так и не написал - упорства и знаний не хватило, но в ходе этой попытки научился очень многому.

Logik
Offline
Зарегистрирован: 05.08.2014

ТС у тебя в башке каша, причем с комочками, сырая и недопересоленная. Сначала разберись с файлами проекта, они вообще к ООП ни коим боком, это совсем разные вещи. Есть разбиение проекта на файлы и без ООП, и очень много такого есть, есть ООП без отдельного файла. Это совсем разное, не связанное. А лет через 5-10 может и про библиотеки можно задуматься будет. Не спеши с этим, хорошая либа должна уметь работать в разных проектах без внесения изменений в нее и не мешать остальному. Это сложно. Потому хорошая либа - редкость большая.

Проект делят на файлы по соображениям минимума связей между файлами и логической завершенности каждого файла. Как правило оба критерия совпадают, хотя есть и исключения. 

 SoftwareSerial  - яд. Он не работает почти что вообще. Любой проект где оно как бы работает со слов автора - недотестированный,  глючит хоть изредка, но зато обязательно.

Про "нескольку сотен строк кода" - спасибо, поржал. А несколько тысячь строк не хош?! В небольшом проекте с пяток таких файлов. Не парся по пустякам.

 

kost82
Offline
Зарегистрирован: 30.11.2015

Писать библиотеку для этого дела я вообще не собирался, не дорос я еще до написания библиотек. Я просто хочу удобства в написании и редактировании скетча. Меня немного напрягает, когда делаешь правку в одном куске кода, а потом крутишь колесом мыши, чтобы сделать правку в другом куске, связанном с этим. Причем я понимаю, что код еще будет разрастаться. А еще меня напрягает дублирование кода, пусть с небольшими изменениями, но все-таки дублирование.

Попробую сначала написать обособленные функции и распихать их в файлы, лежащие в папке скетча.

Logik пишет:

 SoftwareSerial  - яд. Он не работает почти что вообще. Любой проект где оно как бы работает со слов автора - недотестированный,  глючит хоть изредка, но зато обязательно.


Подскажите тогда как быть, если к одной ардуине надо подключить по UART несколько устройств (у меня это модем и RS485 - адаптер)? Что использовать вместо SoftwareSerial? Про Мегу с ее четырьмя последовательными портами я знаю, но мне кажется избыточным использовать такую махину с 50+ цифровыми пинами из которых будут задействованы только 5-6 штук.

nik182
Offline
Зарегистрирован: 04.05.2015

Блюпил.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

я использую SomeSerial но она только для некоторых частот кварца, 16 мегагерц - есть

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

SoftSerial нормально работает если один экземпляр запущен. Подглючивают только если их несколько одновременно забубенить. У меня уже больше года  Nano<--SS-->ESP8266 очень плотно коммуницируют. Никаких глюков за все время. Ну да, пришлось простенький контроль целостности данных для надежности допилить, но это и с железным USART не помешает

Logik
Offline
Зарегистрирован: 05.08.2014

//Подскажите тогда как быть

Наилучший вариант - таки выбирать контроллер с нужным набором переферии. Два uart совсем не экзотика. Например atmega128, поищите по форуму, тут были дельные советы по ещё применению. Ещё клон 328p новый китайский с двумя uart вроде есть. 

Вариант посложней - добавляем контроллер uart подключаемый по i2c или spi. Такие есть готовые но несложно и вторую 328p на эту роль подрядить.

Ещё вариант. Посмотреть и попробовать другие библиотеки программно реализующие uart. Они все хвалятся что лучше чем softwaresirial, это в принципе реально. Критерий отбора - при закоротке rx и tx идёт стабильный приём того что отправляется. Т.е. данные пробегают по кругу и возвращаются без потерь. Если ещё на это не влияет аппаратный сириал - годно. но скорость будет низкая.

Еще вариант. Присмотреться к своей системе. Если там обычный rs485 на одной витой паре, то критерий из предыдущего абзаца тут не актуален, приём и передача одновременно не актуальны, можно softwaresirial применит для 485, а аппаратный на модем. Этот подход ещё и хорош для определения незанятости канала 485го. Но либу скорей всего придётся править. И быть готовым к периодическим глюкам.

Logik
Offline
Зарегистрирован: 05.08.2014

Rumata пишет:

SoftSerial нормально работает если один экземпляр запущен. Подглючивают только если их несколько одновременно забубенить. У меня уже больше года  Nano<--SS-->ESP8266 очень плотно коммуницируют. Никаких глюков за все время. Ну да, пришлось простенький контроль целостности данных для надежности допилить, но это и с железным USART не помешает

Так esp - это не модем. Да и протокол свой с контролем целостности - не стандартный АТ. Если с esp по схеме запрос - ответ, то вполне. А модем может в любой момент времени известить нас о своем событии: входящие звонок, сообщение, сеть лягла и т.д. И если в этот же момент МК вздумает отправить команду то возникает двусторонний обмен. А SS его не может, сообщения бются контроля целостности нет. Отсюда пропуски звонков, СМС и пр. глюки. Из вышесказанного понятно что проблема возникает не часто и зависит от кучи подробностей, но она похоже не устранима.

Ещё на качество приёма влияет загруженность остальных прерываний, с такими же последствиями в целом.

kost82
Offline
Зарегистрирован: 30.11.2015

У меня запросы разделены по времени: сначала делаю запрос по rs485, жду ответа, затем из ответа достаю нужные данные и отправляю их на сервер через модем и жду ответа от сервера. Прием звонков не планируется. Максимум что будет - прием СМС с настройками GPRS соединения (точка доступа, логин, пароль) - но это однократно за долгий период работы. Ну и СМС не обязательно сразу же принимать, можно в свободное от работы время.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

kost82 пишет:

Меня немного напрягает, когда делаешь правку в одном куске кода, а потом крутишь колесом мыши, чтобы сделать правку в другом куске, связанном с этим. 

Это индусская архитектура.  Используй парадигму: "Одна сущность - одна единица трансляции(модуль, файл)". 

5N62V
Offline
Зарегистрирован: 25.02.2016

Logik пишет:

Наилучший вариант - таки выбирать контроллер с нужным набором переферии. 

"Я сейчас тибе адын крамольный весчь скажю. Только ты не абижайся! "  :)))

хотите 6 уартов, или 6 i2c, или 6 SPI? Или любую комбинацию из перечисленного? - берите SAMD21 !

Но, возможный вариант событий таков: вы его возьмете, протрахаетесь дней 5-6, обложите всеми болтами ардуино, напишите свой вариант-файл, и дальше, в зависимости от настойчивости и опыта, либо добьетесь чего хотели, либо забьете и возьмете Мегу(а лучше Дуе), ну либо протрахаетесь еще недельку. 

Моя мысля об том, что брать "контроллер с нужным набором переферии" - это конечно правильно, но при условии, что ты с умеешь управляться с этими контроллерами легко и непринужденно. А это, как ни крути, знания и опыт, которые стоит приобретать заранее, а не когда петух клюнул. Поэтому, если есть возможность взять чего-то покруче чем атмега328 (к примеру) , стоит брать чтоб расширить свой набор способов решения задач. 

 

kost82
Offline
Зарегистрирован: 30.11.2015

5N62V пишет:

Моя мысля об том, что брать "контроллер с нужным набором переферии" - это конечно правильно, но при условии, что ты с умеешь управляться с этими контроллерами легко и непринужденно. А это, как ни крути, знания и опыт, которые стоит приобретать заранее, а не когда петух клюнул. Поэтому, если есть возможность взять чего-то покруче чем атмега328 (к примеру) , стоит брать чтоб расширить свой набор способов решения задач. 

Поддерживаю эту точек зрения. Конечно хорошо бы взять какой-нибудь контроллер покруче, но нужно какое-то время, чтобы к нему привыкнуть, изучить нюансы работы с ним. Например заливка скетча, библиотеки, толерантность к 5 вольтам и т.п. И лучше бы начинать знакомство с проекта попроще.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

kost82 пишет:

Подскажите тогда как быть, если к одной ардуине надо подключить по UART несколько устройств (у меня это модем и RS485 - адаптер)? Что использовать вместо SoftwareSerial? Про Мегу с ее четырьмя последовательными портами я знаю, но мне кажется избыточным использовать такую махину с 50+ цифровыми пинами из которых будут задействованы только 5-6 штук.

Использовать HardwareSerial.

Вот представьте, Вам нужно упаковать груз длиной 2.2 м, а в наличии только ящики по 2 м и 5 м.

5 м, конечно, избыточно, но в 2 м ящики груз не помещается.

Есть, правда, еще вариант ящиков на 2.5 м, но раньше Вы с такими никогда не работали. 

В общем, вариантов, как всегда, больше одного, но при этом 2 м - совсем не вариант.

kost82
Offline
Зарегистрирован: 30.11.2015

Если продолжать аналогию с ящиками, то софтовый сериал - это положить груз в ящик 2м, а оставшиеся 20 см. обмотать скотчем и синей изолентой :-)

Green
Offline
Зарегистрирован: 01.10.2015

andriano пишет:

kost82 пишет:

Подскажите тогда как быть, если к одной ардуине надо подключить по UART несколько устройств (у меня это модем и RS485 - адаптер)? Что использовать вместо SoftwareSerial? Про Мегу с ее четырьмя последовательными портами я знаю, но мне кажется избыточным использовать такую махину с 50+ цифровыми пинами из которых будут задействованы только 5-6 штук.

Использовать HardwareSerial.


HardwareSerial, как наиболее загруженный. А программный для вспомогательных целей (настройка, отладка).
Была задачка с 3-мя UART-ами + пассивный бипер. Едва выкрутился на обычной Ардуино.)

Logik
Offline
Зарегистрирован: 05.08.2014

5N62V пишет:

Моя мысля об том, что брать "контроллер с нужным набором переферии" - это конечно правильно, но при условии, что ты с умеешь управляться с этими контроллерами легко и непринужденно. А это, как ни крути, знания и опыт, которые стоит приобретать заранее, а не когда петух клюнул. Поэтому, если есть возможность взять чего-то покруче чем атмега328 (к примеру) , стоит брать чтоб расширить свой набор способов решения задач. 

 

Та ниче тут крамольного. Тыж заметь, я ему что советовал 128-й и клон 328-года. Не stm там всякие, а максимально близкое к родному. Зоопарк заводить - та ну его в пень. Сам вполне обхожусь линейкой 328й, esp8266 , OrangePi. Плюс клон от вавгета. Но если бы припекло то что-то близкое к каждому из перечисленных взял бы.

vde69
Offline
Зарегистрирован: 10.01.2016

Основной файл:

0. краткое описание проекта (по существу манифест)

1. подключение библиотек

2. создание глобальных переменных, в том числе и переменных собственных стилей

3. инициализация setup

4. основной цикл loop

примерно так:

// ------------------------------------------------------------
// проект "Модульный умный дом (регистратор датчиков)", главный модуль проекта
//
// состав:
// Arduino NANO
// остальные модули подключаются через #define
//
// версия среды Arduino 1.8.14, 
// 
// автор vde69@mail.ru (с)
// ------------------------------------------------------------


// ------------------------------------------------------------
//                   конфигурация прошивки                  
//
// перечень используемых модулей
// ------------------------------------------------------------

// для контроллера NANO используются
// интерфейс I2C:  SDA=A4; SCL=A5
// интерфейс SPI:  MOSI=D11; MISO=D12; SCK=D13; D10 - нельзя использовать кроме как один из селектов устройства этой шины
// интерфейс ONE_WIRE:  D3 (может быть замена)

// для контроллера MEGA используются
// интерфейс I2C:  SDA=D20; SCL=D21
// интерфейс SPI:  MOSI=D51; MISO=D50; SCK=D52; D53 - нельзя использовать кроме как один из селектов устройства этой шины
// интерфейс ONE_WIRE:  D3 (может быть замена)

#define DEVICE_RTC_I2C_DS1307                 // установлен модуль RTC подключение I2C, контакты A4,A5
#define DEVICE_LCD_4X20_I2C                   // установлен LCD экран 4 строки, 20 символов, подключение I2C, контакты A4,A5
//#define DEVICE_BAROMETRIC_SPI_BMP280 4      // установлен датчик давления BMP280, подключение SPI, селект D4
#define DEVICE_TEMPERATURE_ONE_WIRE_DS18B20 3 // установлено 2 датчика температуры Dallas на шину One Wire (шина должна быть активирована одельно через ONE_WIRE)
#define DEVICE_LOGER_MICRO_SD 5               // установлена карта SD для ведения логов, подключение SPI, селект D5
#define MODULE_CALL_TRANSFER 2                // используем модуль расчета теплоотдачи для двух пар датчиков

#include "A_Config.h"           

// ------------------------------------------------------------
//     расширения и общие данные для всех подключаемых классов (модулей)
//
// вынесены в отдельные 2 файла "include/addon.h" и "include/addon.cpp"
// с целью возможности использовать данные как в основной сборке так и при
// синтаксическом контроле подключаемых классов расположеных в каталоге "include/"
// ------------------------------------------------------------

#include <EEPROM.h>
#include <addon.h>
#include <Wire.h>

#ifdef ONE_WIRE 
  #include <OneWire.h> 
#endif 

#ifdef SPI_USE
  #include <SPI.h>
#endif 

#ifdef SD_USE
  #include <SD.h>
#endif 


// ------------------------------------------------------------
// загрузка библиотек, желательно загрузить все библиотеки до первого оператора
// при более позденй загрузки возможны неочевидные ошибки компиляции, 
// очередность загрузки библиотек важен!!!
// до загрузки библиотек не должно быть ничего кроме директив компилятору!!!

#ifdef DEVICE_RTC_I2C_DS1307
//  #include "include/rtc_I2C_DS1307.h"
  #include "rtc_I2C_DS1307.h"
#endif 

#ifdef DEVICE_TEMPERATURE_ONE_WIRE_DS18B20 
//  #include "include/temperature_OneWire_DS18B20.h"
  #include "temperature_OneWire_DS18B20.h"
#endif 

#ifdef DEVICE_BAROMETRIC_SPI_BMP280 
  #include "barometric_SPI_BMP280.h"
#endif 

#ifdef DEVICE_LOGER_MICRO_SD
  #include "Loger_SPI_SD.h";
#endif 

#ifdef MODULE_CALL_TRANSFER
  #include "Call_transfer.h";
#endif 

#ifdef DEVICE_LCD_4X20_I2C 
//  #include "include/lcd_I2C_4x20.h";
  #include "lcd_I2C_4x20.h";
#endif 




// ------------------------------------------------------------
// Общие переменные не зависящие от конфигурации устройства
uint8_t Status              = 0;    // битовый статус контроллера
unsigned long time_loop_new = 0;    // время начала цикла, глобальное время используется в модулях мастера и слейва

// ------------------------------------------------------------
// определение переменных и настроек для шин ONE WIRE
#ifdef ONE_WIRE 
  OneWire One_Wire(ONE_WIRE);     // на пине установленом в настройках
#endif 

// ------------------------------------------------------------
// определение переменных и настроек работы с датой и временем
#if defined(DEVICE_RTC_I2C_DS1307)
  RTC_I2C_DS1307 DT;                
#elif defined(DATE_TIME_USE)
  addon_DateTime DT;                
#endif 

// ------------------------------------------------------------
// определение переменных и настроек для LCD текстового дисплея 4х40
#ifdef DEVICE_LCD_4X20_I2C 
  Lcd_I2C_4x20 lcd_text(9); // резервируем место на 9 числовых параметров для вывода на экран
#endif 

// ------------------------------------------------------------
// определение переменных и настроек для датчиков температуры DS18B20
#ifdef DEVICE_TEMPERATURE_ONE_WIRE_DS18B20
  DeviceAddress DS_Address;
  temperature_OneWire_DS18B20 TT(&One_Wire, &DT);
#endif 

// ------------------------------------------------------------
// определение переменных и настроек для модуля расчета
#ifdef MODULE_CALL_TRANSFER
  Call_Transfer CT(&DT);
#endif 


// ------------------------------------------------------------
// определение переменных и настроек для логирования
#ifdef DEVICE_LOGER_MICRO_SD
  Loger_SPI LG(&DT);
#endif 

// ------------------------------------------------------------
// инициализация процедур конвертации межу источником и получателем данных
// должно идти после определения всех источников данных и до инициализации в получателях
#include "convert.h"



void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);                                           // Инициализация аппаратного UART на скорости 9600
  Serial.println("System START...");                             // Вывод сообщения начла работы

  #ifdef DEVICE_RTC_I2C_DS1307
    Wire.begin(); // не забываем - шина может быть нужна и другим
    DT.begin();
    //DT.SetDateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t sec) 
    
    //DateTime(__DATE__, __TIME__);
    
  #endif 

  #ifdef DATE_TIME_USE || DEVICE_RTC_I2C_DS1307
    // тут по идеи надо получить правильную дату и время, 
    // для этого можно использовать переменные __DATE__, __TIME__ или данные из флеш памяти
    // пока не используем...
    //
    //DT.SetDateTime (2010, 10, 10, 10, 10, 10); 
  #endif 

  #ifdef DEVICE_TEMPERATURE_ONE_WIRE_DS18B20 
    // инициируем датчики, в дальнейшем обращение к ним будет через индексы массыва
    DS_Address[0]=0x28; DS_Address[1]=0x62; DS_Address[2]=0x7D; DS_Address[3]=0xD6; DS_Address[4]=0x04; DS_Address[5]=0x00; DS_Address[6]=0x00; DS_Address[7]=0xA4;
    TT.initSensor(0, DS_Address);  // датчик с индексом 0 = 

    DS_Address[0]=0x28; DS_Address[1]=0x70; DS_Address[2]=0x02; DS_Address[3]=0xDD; DS_Address[4]=0x04; DS_Address[5]=0x00; DS_Address[6]=0x00; DS_Address[7]=0x07;
    TT.initSensor(1, DS_Address);  // датчик с индексом 1 = 

    DS_Address[0]=0x28; DS_Address[1]=0x02; DS_Address[2]=0x7D; DS_Address[3]=0xA9; DS_Address[4]=0x04; DS_Address[5]=0x00; DS_Address[6]=0x00; DS_Address[7]=0x55;
    TT.initSensor(2, DS_Address);  // датчик с индексом 2 = 
  #endif 

  #ifdef DEVICE_LOGER_MICRO_SD
    LG.init(DEVICE_LOGER_MICRO_SD); 
    #ifdef DEVICE_TEMPERATURE_ONE_WIRE_DS18B20 && DEVICE_LOGER_MICRO_SD
      LG.init_TT(&TT); 
    #endif 
  #endif 

  #ifdef DEVICE_LCD_4X20_I2C 
    // вывод и настройка маски и параметров на экран
    Display_SetPage_01 ();
  #endif 
}

void loop() {
  time_loop_new = millis();  

  #ifdef DATE_TIME_USE || DEVICE_RTC_I2C_DS1307
    // отвечает за ход внутренних часов, работает автономно, 
    // при подключенном модуле RTC переодически синхронизируется по нему 
    DT.poll(time_loop_new);         
  #endif 
  
  #ifdef DEVICE_TEMPERATURE_ONE_WIRE_DS18B20 
    // отвечает за контроль за температурой
    TT.poll(time_loop_new);
  #endif 

  #ifdef DEVICE_LOGER_MICRO_SD 
    // отвечает за сохранения лога работы
    LG.poll(time_loop_new);
  #endif 

  #ifdef DEVICE_LCD_4X20_I2C 
    // отвечает за вывод на экран параметров
    lcd_text.poll(time_loop_new);
  #endif 
  

}

 

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

То есть стремится нужно к тому, что 1 файл это что-то одно очень конкретное и цельное, ну например ты вынес все функции работы со временем в отдельный файл, вроде удобно, а тут тебе в другом файле понадобилось время, тут дилема или копировать код или вызывать функции из другого модуля. 

Оба эти подхода не верные, правильным будет сделать отдельно библиотеку абстрактных классов (что такое абстрактный класс почитай в инете) и этот указатель на потомок на этот класс передавать туда где он нужен, а там уже в зависимости от условий работать с той веткой которую мы получили, в моем примере так сделано например описание переменной DT, она может быть нескольких разных классов имеющих одного родителя.

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

 

пока писал понял, что написал наверно не очень понятно :) но переписывать не буду

 

 

nik182
Offline
Зарегистрирован: 04.05.2015

Я вот ахреневаю от того, что ООП тащат в маленькие микроконтроллеры. Как то раньше обходились. Вот на днях супер картинку приводили про сравнение АВР и процессора из космоса, считающего траектории. Как то всё было в пользу АВР, кроме программиста. Нет я не против. Нравиться пожалуйста. Только вот МК имеет одну программу. Ни каких ОС. Ограничения по памяти. И существенный порог по образованию при переходе на ООП. Одно дело на ББ. Там без ООП совсем грустно. На лицо существенное ускорение процесса программирования. Но для МК ни разу не потребовалось ООП. 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Только большинство проектов не подразумевает развития и добавления функционала. Сделал,проверил,установил,забыл. Соответственно и ООП там нафиг не нужен.

vde69
Offline
Зарегистрирован: 10.01.2016

andycat пишет:
Только большинство проектов не подразумевает развития и добавления функционала. Сделал,проверил,установил,забыл. Соответственно и ООП там нафиг не нужен.

не совсем так... сделал станок ЧПУ и потом еще года 3 его модифицируешь, то свет добавишь, то алгоритмы улучшишь, то концевик заменишь вместо кнопки поставишь сверх точный цифровой.

Да в любом проекте так, сделал и забыл это скорее исключение...

Green
Offline
Зарегистрирован: 01.10.2015

На Ардуино обычно проекты относительно мелкие и поддерживать их не составляет большого труда. Из своей практики, за последние 6 лет ко мне обращалось буквально пару заказчиков для изменения кода. Думаю, многие просто осваивают сами.

b707
Offline
Зарегистрирован: 26.05.2017

vde69 пишет:

То есть стремится нужно к тому, что 1 файл это что-то одно очень конкретное и цельное, ну например ты вынес все функции работы со временем в отдельный файл, вроде удобно, а тут тебе в другом файле понадобилось время, тут дилема или копировать код или вызывать функции из другого модуля. 

Оба эти подхода не верные, правильным будет сделать отдельно библиотеку абстрактных классов

поясните, почему "вызывать функции из другого модуля" - неверный подход? Чем это отличается от использования библиотек?

Вот, например, у меня в одном из проектов может на выбор использоваться связь по NRF,  CAN или RS485. Чтобы не усложнять код, всю работу с конкретными протоколами я вынес в отдельные файлы, создав для этих библиотек однотипный интерфейс. условно с двумя функциями my_Send()  my_Receive().

Теперь все, что мне нужно, чтобы перенастроить работу с CAN на NRF24 - это поменять include "my_Can.h" на include "my_NRF.h". И чем это плохо?

Я понимаю, что можно это написать и через абстрактный класс. Только зачем?

 

vde69
Offline
Зарегистрирован: 10.01.2016

я писал плохо делать так:

 

основная программа - О

Модуль - 1

Модуль - 2

 

Так вот плохо из модуля-1 вызывать процедуры которые находятся в модуле-2 напрямую, правильно делать так

В основном модуле создаем обьект который описан в модуле-2 а в модуле-1 использовать этот объект созданный в основном модуле... В этом случае интерфейс отделяется от фактической реализации...

b707
Offline
Зарегистрирован: 26.05.2017

vde69 пишет:

В основном модуле создаем обьект который описан в модуле-2 а в модуле-1 использовать этот объект созданный в основном модуле... В этом случае интерфейс отделяется от фактической реализации...

использовать в модулях обьекты. созданные в основной программе - ровно так же плохо, как и в других модулях. Правильное разбиение программы, это когда в модуле-1 используются только обьекты и методы модуля-1, а в модуле-2 - соответственно только его .

А основная программа вызывает процедуры из обоих модулей. Именно о таком разбиении я и писал в своем примере.

vde69
Offline
Зарегистрирован: 10.01.2016

в модуле используется ссылка(указатель) на объект, например так сделана библиотека датчиков далласа, там в нее передается экземпляр объекта OneWire который инициализируется в основном модуле.

То есть надо учитывать совместное использование. Вот у меня например есть объект который работает с датами, от него наследник который работает с датами и заодно с платой часов реального времени а другой объект будет синхронизироватся с интернетом, в результате имеем 3 разных варианта объекта, и используем их в 7 разных модулях.

Densl
Offline
Зарегистрирован: 28.11.2018

/*Я просто хочу удобства в написании и редактировании скетча. Меня немного напрягает, когда делаешь правку в одном куске кода, а потом крутишь колесом мыши, чтобы сделать правку в другом куске, связанном с этим. Причем я понимаю, что код еще будет разрастаться. А еще меня напрягает дублирование кода, пусть с небольшими изменениями, но все-таки дублирование.

*/

Для удобстватредактирования можно использовть внешний редактор notepad++. Я так делаю

5N62V
Offline
Зарегистрирован: 25.02.2016

Densl пишет:

Для удобстватредактирования можно использовть внешний редактор notepad++. Я так делаю

У Visual Studio и у Atmel Studio  есть платный ардуиновский плагин. Стоит 12 баксов в год. Пользую, удобно, рекомендую. 

Densl
Offline
Зарегистрирован: 28.11.2018

5N62V пишет:

Densl пишет:

Для удобстватредактирования можно использовть внешний редактор notepad++. Я так делаю

У Visual Studio и у Atmel Studio  есть платный ардуиновский плагин. Стоит 12 баксов в год. Пользую, удобно, рекомендую. 


Это не для меня, я комп дома редко включаю, а на работе кроме блокнота админы запрещают ставить другие проги. Вот на работе пишу в ноутпаде, дома редактирую в нем и шью через IDE. Знаю, есть куча других удобных способов, но пока не до них. В ноутпаде тоже удобно.
Гораздо важнее для меня правильно структурировать программу, чтобы если что-то поменять в функциональности, не приходилось весь код заново отлаживать. Поэтому в больших проектах без ООП делать нечего я считаю.

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

5N62V пишет:

У Visual Studio ..... есть платный ардуиновский плагин.

Именно у VS? Не у VSC? Оно умеет с есп работать? СДК еспшная нормально подхватывается? Я чет с бесплатным VSCшным плагином так и не нашел общего языка. 

 

b707
Offline
Зарегистрирован: 26.05.2017

Rumata пишет:

Именно у VS? Не у VSC? Оно умеет с есп работать? СДК еспшная нормально подхватывается?

У Visual Studio

Подхватывает все установленные в Ардуино ИДе библиотеки и cores для других МК. То есть умеет работать со всем, с чем умеет оригинальная Ардуино ИДЕ.

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

b707 пишет:

Подхватывает все установленные в Ардуино ИДе библиотеки и cores для других МК. То есть умеет работать со всем, с чем умеет оригинальная Ардуино ИДЕ.

Это по крайней мере звучит - супер. Если не сложно, можно ссылочку на плагин? 

b707
Offline
Зарегистрирован: 26.05.2017

Rumata пишет:

Это по крайней мере звучит - супер. Если не сложно, можно ссылочку на плагин? 

https://www.visualmicro.com/

В "Отвлеченных" есть тема, посвященная VMicro. Там, кстати, народ искал с кем бы купить лицензию "на троих"

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

на пятерых лучше