Detect hardware before setup, or, recreating clients in run time

I am using MKR WIFI and MKR ETH and my program allows me to compile for WiFI or ETH connectivity by selecting the relevant definition at the top:

#define WIFI 
//#define ETH

#include <ArduinoMqttClient.h>
  
#ifdef WIFI
  #include <WiFiNINA.h>
  WiFiClient wifiClient;
  MqttClient mqttClient(wifiClient);
  WiFiSSLClient httpsClient;
  WiFiClient transport;
#endif

#ifdef ETH
  #include <Ethernet.h>
  EthernetClient ethClient;
  MqttClient mqttClient(ethClient);
  EthernetClient httpsClient;
  EthernetClient transport;
#endif

It works well, however, I can't select connectivity without re-flashing and I would like to add that capability by assigning a pin and based on its state decide if to connect using WiFi or ETH.

I want to get rid of the WIFI or ETH constants and have both classes compile together.

I know I can use different names for WiFI and ETH classes and add a condition in the dozens locations where I call each of the classes but this looks like the worst solution.

I tried leaving WiFi classes before setup() and moved ETH classes inside setup() where I can detect the pin but it didn't work, when the pin was set for ETH Mode the ETH Client was unable to connect.

My questions:

  1. Is it possible to read hardware state in the variable declaration section?

  2. Is it possible to re declare classes in setup?

  3. Am I missing a simpler solution?

Thanks!

You could just create your own class that contains 1 instance of a WiFi client and one instance of an Eithernet client and a flag to declare which type is being used. To save space, you could even make them a union

I've never used either of those libraries. But, it looks like both the WiFiClient and EthernetClient classes inherit from the Client class. So, maybe polymorphism would work:

#include <Ethernet.h>
#include <WiFiNINA.h>

Client *commonClient;

void setup() {
  if (chooseEthernet) {
    EthernetClient *ethClient = new EthernetClient;
    // Do any setup required for Ethernet
    commonClient = ethClient;
  } else {
    WiFiClient *wifiClient = new WiFiClient;
    // Do any setup required for WiFi
    commonClient = wifiClient;
  }
}

You'd then use the 'commonClient' pointer to access the client created in setup().

of course.
on my last project at qualcomm we needed to support various RF hardware. there were several bits read at startup to ID the hardware.

later bits in a small serial eeprom were used, configured at the factory

on other projects (DSP speakerphone) optional pull-up/pull-downs on output pins were read before configuring the pins as outputs to determiner book mode.

you won't be able to redefine a variable as a different types. there are such things as virtual functions. The serial interface can support use of either UART or Bluetooth

This is very useful.
I am just having troubles declaring the MQTT client, originally written
MqttClient mqttClient(wifiClient);
Can you please suggest the correct syntax in the context of your offer?
Thanks!

Any idea how? I couldn't find any information on how to access the hardware before setup().

It looks like MqttClient has a constructor overload that takes a pointer to a Client:

class MqttClient : public Client {
public:
  MqttClient(Client* client);

A trick I use is to create global pointers to all the resources you would/could need. Then, in setup() you create the subset you will actually need at that time dynamically. This way you don't need to rely on anything before setup(). (And you don't want to rely on that "before setup()" stuff)

In fact, this method ended up working so well that I went the next level and made my own class that was a sketch. I base all my handhelds on this. The user selects what she wants to do and the main program spans the sketch to handle it. Its a really slick system.

-jim lee

Can you use the defines that the build process includes for you? That way the code will be compiled for whichever board it is being uploaded to. No manual switching required.

ARDUINO_SAMD_MKRWIFI1010
ARDUINO_SAMD_MKRWAN1300
ARDUINO_SAMD_MKRWAN1310

#if defined(ARDUINO_SAMD_MKRWIFI1010)
  #include <WiFiNINA.h>
  WiFiClient wifiClient;
  MqttClient mqttClient(wifiClient);
  WiFiSSLClient httpsClient;
  WiFiClient transport;
#endif

#if defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310)
  #include <Ethernet.h>
  EthernetClient ethClient;
  MqttClient mqttClient(ethClient);
  EthernetClient httpsClient;
  EthernetClient transport;
#endif

Problem with that is that the hardware always have both MKR 1010 and ETH Shield and I need to allow selecting connectivity in the field.

Understood (to some extent) , but I still don't know how to syntax that in your solution.

Ah... So you have the "MKR WiFi 1010" and optionally put a "MKR ETH SHIELD" on it. Do you put the ETH Shield on even if you are using WiFi? If not, you might be able to just check for the ETH Shield and assume WiFi if the shield is not present. This code can detect the Ethernet chip type:

  Ethernet.begin(mac, ip);

  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.");
  }
  else if (Ethernet.hardwareStatus() == EthernetW5100) {
    Serial.println("W5100 Ethernet controller detected.");
  }
  else if (Ethernet.hardwareStatus() == EthernetW5200) {
    Serial.println("W5200 Ethernet controller detected.");
  }
  else if (Ethernet.hardwareStatus() == EthernetW5500) {
    Serial.println("W5500 Ethernet controller detected.");
  }

If it comes back with a value of EthernetNoHardware then you should use WiFi.

And if you always have the shield, you can use "Ethernet.linkStatus()" to tell if the Ethernet cable is connected or not.

If the Ethernet cable is always connected to a network but you want to use WiFi, connect a switch to an input pin and flip the switch to select between the two networks.

Sorry John, I guess it wasn't clear in my previous response. I always have both, MKR + ETH, this is how I package it, always, and later in the field a decision is taken to use it with Ethernet cable or just WiFi.

Right, I have that switch on a pin and I am trying to find a way to either detect pin state before setup() or in setup() declare the classes as gfvalvo suggested above (the difficulty I have with that is the syntax for the MQTT Class).

This compiles without errors or warnings. To use, change "mqttClient.function()" to "p_mqttClient->function()".

#include <ArduinoMqttClient.h>

#include <WiFiNINA.h>
#include <Ethernet.h>

const byte NetworkSwitchPin = 5;

WiFiClient    WiFi_Client;
WiFiSSLClient WiFi_httpsClient;
WiFiClient    WiFi_transport;

EthernetClient Eth_Client;
EthernetClient Eth_httpsClient;
EthernetClient Eth_transport;

MqttClient *p_mqttClient;

void setup()
{
  if (digitalRead(NetworkSwitchPin) == LOW)
  {
    p_mqttClient = new MqttClient(Eth_Client);
  }
  else
  {
    p_mqttClient = new MqttClient(WiFi_Client);
  }

}

void loop() {}

Thats worked really well, thanks a million!

You don't need to instantiate both the WiFi and Ethernet objects. You can dynamically choose one of them:

#include <ArduinoMqttClient.h>
#include <Ethernet.h>
#include <WiFiNINA.h>

Client *commonClient;
MqttClient *p_mqttClient;

void setup() {
  if (chooseEthernet) {
    EthernetClient *ethClient = new EthernetClient;
    // Do any setup required for Ethernet
    commonClient = ethClient;
  } else {
    WiFiClient *wifiClient = new WiFiClient;
    // Do any setup required for WiFi
    commonClient = wifiClient;
  }

  p_mqttClient = new MqttClient(commonClient);
}

void loop() {
}

Cool, thanks a lot.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.