JiriBilek / WiFiSpi

SPI library for Arduino AVR and STM32F1 to connect to ESP8266
GNU Lesser General Public License v3.0
62 stars 13 forks source link

Problems using ArduinoMDNS with WiFiSpi #27

Closed fredlcore closed 3 years ago

fredlcore commented 3 years ago

I'm trying to get the ArduinoMDNS example for registering a service working with WiFiSpi. The original sketch can be found here: https://github.com/arduino-libraries/ArduinoMDNS/blob/master/examples/Ethernet/RegisteringServices/RegisteringServices.ino I have adapted it to work with WiFiSpi and WiFiSpiUdp and it compiles fine, but after the mdns.addServiceRecord("Arduino mDNS Webserver Example._http", 80, MDNSServiceTCP); line, the debug output of the Arduino (Due) is filled with these lines:

[espspi_proxy.h:329] W: Slave tx is not ready, status 0                         
[srvspi_drv.cpp:262] W: Error waitResponse                                      
[espspi_proxy.h:329] W: Slave tx is not ready, status 0                         
[srvspi_drv.cpp:262] W: Error waitResponse                                      
[espspi_proxy.h:329] W: Slave tx is not ready, status 0                         
[srvspi_drv.cpp:262] W: Error waitResponse 

The sketch seems to stay in some kind of infinite loop repeating these messages.

Is WiFiSpiUdp (significantly) different from EthernetUDP, so that the mDNS library cannot be used with WiFiSpi? Or is this (hopefully) just a misconfiguration or bug that can be fixed?

Here's the test case:

//  Illustrates how to register a Bonjour service.

#include <SPI.h>
#include "src/WiFiSpi/src/WiFiSpi.h"
#include "src/WiFiSpi/src/WiFiSpiUdp.h"
#include "src/ArduinoMDNS/ArduinoMDNS.h"

WiFiSpiUdp udp;
MDNS mdns(udp);

// you can find this written on the board of some Arduino Ethernets or shields
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 

char ssid[] = "WiFiNetwork";        // your network SSID (name)
char pass[] = "Password";     // your network password (use for WPA)

int status = WL_IDLE_STATUS;
WiFiSpiServer server(80);

void setup()
{
  Serial.begin(115200);
  Serial.println("Go...");
  WiFiSpi.init();

 if (WiFiSpi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue:
    while (true);
  }

  if (!WiFiSpi.checkProtocolVersion()) {
    Serial.println("Protocol version mismatch. Please upgrade the firmware");
    // don't continue:
    while (true);
  }

  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open network:
    status = WiFiSpi.begin(ssid, pass);
Serial.println(status);
    if (status != WL_CONNECTED)
        Serial.println("Cannot connect to AP.");
        // don't continue if connection failed
  }
  delay(3000);
  Serial.println("Connected to wifi");
  Serial.println(WiFiSpi.localIP());

  server.begin();

  // Initialize the mDNS library. You can now reach or ping this
  // Arduino via the host name "arduino.local", provided that your operating
  // system is mDNS/Bonjour-enabled (such as MacOS X).
  // Always call this before any other method!
  mdns.begin(WiFiSpi.localIP(), "arduino");

  // Now let's register the service we're offering (a web service) via mDNS!
  // To do so, we call the addServiceRecord() method. The first argument is the
  // name of our service instance and its type, separated by a dot. In this
  // case, the service type is _http. There are many other service types, use
  // google to look up some common ones, but you can also invent your own
  // service type, like _mycoolservice - As long as your clients know what to
  // look for, you're good to go.
  // The second argument is the port on which the service is running. This is
  // port 80 here, the standard HTTP port.
  // The last argument is the protocol type of the service, either TCP or UDP.
  // Of course, our service is a TCP service.
  // With the service registered, it will show up in a Bonjour-enabled web
  // browser. As an example, if you are using Apple's Safari, you will now see
  // the service under Bookmarks -> Bonjour (Provided that you have enabled
  // Bonjour in the "Bookmarks" preferences in Safari).
  mdns.addServiceRecord("Arduino mDNS Webserver Example._http", 80, MDNSServiceTCP);
}

void loop()
{ 
  // This actually runs the mDNS module. YOU HAVE TO CALL THIS PERIODICALLY,
  // OR NOTHING WILL WORK! Preferably, call it once per loop().
  mdns.run();

  // The code below is just taken from the "WebServer" example in the Ethernet
  // library. The only difference here is that this web server gets announced
  // over mDNS, but this happens in setup(). This just displays something
  // in the browser when you connect.
  WiFiSpiClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    bool current_line_is_blank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        // if we've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so we can send a reply
        if (c == '\n' && current_line_is_blank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();

          client.println("Hello from a mDNS-enabled web-server running ");
          client.println("on your Arduino board!");

          break;
        }
        if (c == '\n') {
          // we're starting a new line
          current_line_is_blank = true;
        } else if (c != '\r') {
          // we've gotten a character on the current line
          current_line_is_blank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    client.stop();
  }
}
fredlcore commented 3 years ago

ArduinoMDNS states that

any Arduino core and networking library that supports the new virtual UDP::beginMulticast(...) method

would be working. Could that be the root of the problem?

JiriBilek commented 3 years ago

You're absolutely right. This function is not implemented and from the inner class UDP returns always 0. I don't know what should it look like, nor what should it do. Please hold on, I'll look at it.

fredlcore commented 3 years ago

That would be awesome, thanks!

JiriBilek commented 3 years ago

I am sorry, I haven't looked at it yet. It requires some investigation what actually the multicast is and how could I test it. I am unable to find an example for testing the multicast. Do you have any?

fredlcore commented 3 years ago

No worries, I'm glad you're looking into it! As for what Multicast is, here are a few links with examples that might be helpful (if a destination IP is used, it has to be of the multicast address range, i.e. 224.0.0.0 through 239.255.255.255. ArduinMDNS uses 224.0.0.251):

Arduino UDP Multicast send/receive: https://tkkrlab.nl/wiki/Arduino_UDP_multicast Multicast tutorial: https://community.particle.io/t/multicast-udp-tutorial/19900 Sender and listener for Linux written in C++: https://gist.github.com/hostilefork/f7cae3dc33e7416f2dd25a402857b6c6

Other than that, this is the beginMulticast-code from EthernetUdp.cpp (the "makesocket" section is releant here, I guess, and most of it is calculating the MAC address based on the Multicast IP address):

// multicast version to set fields before open  thd
uint8_t EthernetClass::socketBeginMulticast(uint8_t protocol, IPAddress ip, uint16_t port)
{
    uint8_t s, status[MAX_SOCK_NUM], chip, maxindex=MAX_SOCK_NUM;

    // first check hardware compatibility
    chip = W5100.getChip();
    if (!chip) return MAX_SOCK_NUM; // immediate error if no hardware detected
#if MAX_SOCK_NUM > 4
    if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets
#endif
    //Serial.printf("W5000socket begin, protocol=%d, port=%d\n", protocol, port);
    SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    // look at all the hardware sockets, use any that are closed (unused)
    for (s=0; s < maxindex; s++) {
        status[s] = W5100.readSnSR(s);
        if (status[s] == SnSR::CLOSED) goto makesocket;
    }
    //Serial.printf("W5000socket step2\n");
    // as a last resort, forcibly close any already closing
    for (s=0; s < maxindex; s++) {
        uint8_t stat = status[s];
        if (stat == SnSR::LAST_ACK) goto closemakesocket;
        if (stat == SnSR::TIME_WAIT) goto closemakesocket;
        if (stat == SnSR::FIN_WAIT) goto closemakesocket;
        if (stat == SnSR::CLOSING) goto closemakesocket;
    }
#if 0
    Serial.printf("W5000socket step3\n");
    // next, use any that are effectively closed
    for (s=0; s < MAX_SOCK_NUM; s++) {
        uint8_t stat = status[s];
        // TODO: this also needs to check if no more data
        if (stat == SnSR::CLOSE_WAIT) goto closemakesocket;
    }
#endif
    SPI.endTransaction();
    return MAX_SOCK_NUM; // all sockets are in use
closemakesocket:
    //Serial.printf("W5000socket close\n");
    W5100.execCmdSn(s, Sock_CLOSE);
makesocket:
    //Serial.printf("W5000socket %d\n", s);
    EthernetServer::server_port[s] = 0;
    delayMicroseconds(250); // TODO: is this needed??
    W5100.writeSnMR(s, protocol);
    W5100.writeSnIR(s, 0xFF);
    if (port > 0) {
        W5100.writeSnPORT(s, port);
    } else {
        // if don't set the source port, set local_port number.
        if (++local_port < 49152) local_port = 49152;
        W5100.writeSnPORT(s, local_port);
    }
    // Calculate MAC address from Multicast IP Address
        byte mac[] = {  0x01, 0x00, 0x5E, 0x00, 0x00, 0x00 };
        mac[3] = ip[1] & 0x7F;
        mac[4] = ip[2];
        mac[5] = ip[3];
        W5100.writeSnDIPR(s, ip.raw_address());   //239.255.0.1
        W5100.writeSnDPORT(s, port);
        W5100.writeSnDHAR(s, mac);
    W5100.execCmdSn(s, Sock_OPEN);
    state[s].RX_RSR = 0;
    state[s].RX_RD  = W5100.readSnRX_RD(s); // always zero?
    state[s].RX_inc = 0;
    state[s].TX_FSR = 0;
    //Serial.printf("W5000socket prot=%d, RX_RD=%d\n", W5100.readSnMR(s), state[s].RX_RD);
    SPI.endTransaction();
    return s;
}
JAndrassy commented 3 years ago

Jiri, it is used the same way as UDP.begin(). Only send WiFiSpiUdp.beginMulticast to WiFiUdp.beginMulticast of the ESP8266WiFi library.

JiriBilek commented 3 years ago

@fredlcore and Juraj: thank you so much. I hope now I have all the information needed to implement it.

fredlcore commented 3 years ago

You're most welcome, thank you for your effort, and thanks, @jandrassy for this really helpful information!

JiriBilek commented 3 years ago

~~Hi, I realized that on my network, I am not able to use a multicast. It may be an issue of my router (TPLink Archer C5) or whatever. No matter if I try to transmit wired or wireless. Do you have any ideas? I am not able to test UDP Multicast now.~~

Edit: it is not an issue with a router but with the VirtualBox, namely its ethernet adapter. Eventually, I tried it on another PC and it runs. (https://www.virtualbox.org/ticket/8698)

JiriBilek commented 3 years ago

I created new branch here and in the WiFiSpiESP repository named UDP_Multicast. @fredlcore please check them out and test if the library works for you. I also added two examples (multicast transmit and receive).

fredlcore commented 3 years ago

Great! I was just about to give up when the errors came again until I ran the test case above again and it notified me that also the ESP part had changed. So once I flashed the ESP with the new version, it works like charm, thanks so much :)!

Will you merge this directly into the master branch or should I refer people to this branch instead?

JiriBilek commented 3 years ago

Thanks for testing, I'll merge it to the master.

JiriBilek commented 3 years ago

Closed via https://github.com/JiriBilek/WiFiSpi/pull/29