Маршрутизация выхода Arduino Mega к одному из двух последовательных портов

У меня есть контроллер Arduino Mega, который обычно связывается через Bluetooth (модуль Bluetooth HC08) с устройством Android. В случае возникновения проблемы на стороне Android я хочу иметь резервную возможность общаться через последовательный порт USB. Выбор последовательного порта определяется аппаратным коммутатором, который может считывать Arduino. В настоящее время у меня есть:

HardwareSerial & Terminal = Serial;
HardwareSerial & BTPort = Serial1;

Есть ли способ направить выход либо на BTPort, либо на терминал, в зависимости от состояния коммутатора? В идеале я хотел бы сделать что-то вроде этого:

HardwareSerial & OutputPort
if(switch_is_on) 
  OutputPort = Terminal;
else
  OutputPort = BTPort;

Очевидно, что приведенный выше код не работает. Я понятия не имею, как действовать дальше.

, 👍0

Обсуждение

Как и вы, я бы использовал глобальную переменную, например switch_is_on, и написал бы метод printSerial( ... ), содержащий оператор if else, который использует последовательный объект для случая swich_is_on == true и Serial1 для случая else. Я бы использовал этот метод вместо методов печати Serial / Serial1. Кроме того, для меня не очевидно, почему ваша попытка не должна сработать. В чем проблема (кроме пропущенной точки с запятой причины ;-))?, @Peter Paul Kiefer

Спасибо тебе, Питер, за твой комментарий. Да, это решает проблему, но не самым эффективным образом. Было бы наиболее эффективно, если бы можно было просто переназначить (указатель на?) последовательный порт и напрямую использовать println для переназначенного порта. Однако ваше предложение - очень полезный обходной путь., @Willem Ferguson

Как я уже сказал, я не знаю никакой причины, по которой вы не можете назначить Serial или Serial1 переменной типа HardwareSerial &<name>, например HardwareSerial &activeSerial = Serial1; Если вы объявите activeSerial глобальной переменной, вы сможете получить к ней доступ отовсюду. Я просто не знаю, почему очевидно, что это не должно работать. По крайней мере, для меня так и должно быть. Если вы хотите использовать только указатель, используйте HardwareSerial *activeSerial = &Serial и activeSerial-> print ...., @Peter Paul Kiefer

Но я бы предпочел метод printMethod, потому что это самый эффективный способ. Что произойдет, если вы измените флаг переключения во время работы программы? Если у вас есть предопределенная переменная activeSerial, ничего не происходит. Этот метод будет использовать другой последовательный интерфейс. Вы видите эту идею?, @Peter Paul Kiefer

К ВАШЕМУ СВЕДЕНИЮ: https://forum.arduino.cc/index.php?topic=358740.0, @Peter Paul Kiefer

@PeterPaulKiefer, ссылка должна быть назначена при определении, @Juraj

У меня есть: `HardwareSerial *Myserial; Myserial = Serial1;` Это дает ошибку: Myserial не называет тип., @Willem Ferguson

В глобальной области допустима только инициализация, например HardwareSerial *hs = &Serial1;. Или выполните задание в функции., @Mat

@Juraj Спасибо, что указали на это, я не объяснил это ясно. Ссылки (<Type> &) должны быть объявлены и инициализированы вместе, поэтому они не могут быть использованы для вычисления альтернативных ссылок позже. Но у нас нет таких ограничений. Виллем, мне очень жаль, что я не упомянул об этом. Я просто не думал так далеко. ;-), @Peter Paul Kiefer

Проблема решена. Спасибо всем, кто откликнулся., @Willem Ferguson


1 ответ


1

Я могу придумать три различных способа (и один похожий на другой) делать то, что вы хотите.

Но сначала заметка о полиморфизме. Класс HardwareSerial сам по себе является дочерним по отношению к классу Stream. Именно этот класс Stream предоставляет все функции чтения / записи (он сам является дочерним классом Print, который предоставляет все функции print(...)).

Если все, что вы хотите сделать, это распечатать, то вы должны работать с классом Print. если вы хотите делать как чтение, так и запись, то класс Stream - это то, что вам нужно. Вы должны использовать HardwareSerial только в том случае, если вам нужно использовать последовательные функции, такие как .begin(baud).

Итак, имея это в виду, вы могли бы:

1. Назначение указателя

Print *printer;

void setup() {
    Serial.begin(115200);
    Serial1.begin(115200);
    printer = &Serial;
}

void loop() {
    printer = &Serial;
    printer->println(millis());
    delay(100);
    printer = &Serial1;
    printer->println(millis());
    delay(100);
}

Это, безусловно, самый эффективный метод с чисто машинной точки зрения.

Обратите внимание на использование -> вместо . при вызове функции по указателю.

2а. Используйте функцию- оболочку

uint8_t stream = 0;

void printit(const char *text) {
    switch (stream) {
        case 0: 
            Serial.print(text);
            break;
        case 1:
            Serial1.print(text);
            break;
    }
}

void setup() {
    Serial.begin(115200);
    Serial1.begin(115200);
}

void loop() {
    stream = 0;
    printit("Hello on stream 0\r\n");
    stream = 1;
    printit("Hello on stream 1\r\n");
}

Это менее эффективно и ограничивает то, что вы можете печатать, если не пишете много перегруженных функций.

2в. Используйте макрос

Это похоже на 2a, но использует макрос вместо функции:

#define printit(T) if (stream == 0) Serial.print(T) else Serial1.print(T)

Это сохраняет стандартную перегрузку функции печати, но имеет много операторов IF, замусоренных в вашем коде. Немного эффективнее, чем 2a, так как все это встроено. Однако человеку труднее понять это.

3. Создайте класс-оболочку

Это метод роллс-ройса. Все становится инкапсулированным в ваш собственный объект. Указатели используются для того, чтобы поддерживать его эффективность.

class Printer : public Print {
    private:
        Print *stream;
        Print *stream0;
        Print *stream1;
    public:
        Printer(Print &s0, Print &s1) : stream0(&s0), stream1(&s1) {
            stream = &s0;
        }
        size_t write(uint8_t c) {
            return stream->write(c);
        }
        void setStream(uint8_t sno) {
            switch (sno) {
                case 0: stream = stream0; break;
                case 1: stream = stream1; break;
            }
        }
};

Printer printer(Serial, Serial1);

void setup() {
    Serial.begin(115200);
    Serial1.begin(115200);
}

void loop() {
    printer.setStream(0);
    printer.println(millis());
    delay(100);
    printer.setStream(1);
    printer.prinln(millis());
    delay(100);
}

С помощью этого метода новый класс можно поместить в библиотеку и использовать в ваших проектах.

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

И сноска для всех тех, кто говорит: "Зачем использовать указатели? Почему не рекомендации? Я говорю: ненавижу ссылки. Указатели гораздо более очевидны, чем они есть. Ссылки, которые вы не можете отличить от различных переменных. С указателем вы знаете, что это указатель. Ссылки - это отродье дьявола.

,

Это полное объяснение, которое проясняет проблему. Спасибо, что уделили мне время., @Willem Ferguson