Can't get shields to work in combination

Hello Community,
I hope someone can help me, since I'm stuck with this problem for days now.
I have an Arduino Uno R3 and these two Shields:

I want to be able to control the servos via ethernet (python).
I did manage to get each shield on it's own to work, but in combination they don't work.
Well, weirdly, very seldom, they do work in combination...which is weird.

The ethernet shield uses SPI and the servo shield i2c, so I don't think they are in conflict.
The structure:
On the arduino is the ethernet shield (because of SPI connector) and on the ethernet shield (with risers or the ethernet port is in the way) is the servo shield, which is supplied with 5V for the servos.

I really hope someone can help me, I don't know what to do anymore...

If needed this is the code:

#include <SPI.h>
#include <Ethernet.h>

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// ----- GENERAL SETTINGS -----
#define ARDUINO_NR          1U

// ----- WIZNET SETTINGS -----
#define SS                  10U                           //D10 ---- SS
#define RST                 11U                           //D11 -----Reset

// ----- ETHERNET SETTINGS -----
#define DHCP                0                             // Dynamic IP (not working correctly)
#define PORT                8123                          // Port of socket server

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};  // MAC address
IPAddress ip(192, 168, 1, 152);                     // Static IP address

// ----- FAN MEASUREMENT SETTINGS -----
#define ENCODER0_PIN      3
#define ENCODER1_PIN      4

// ----- SERVO SETTINGS -----
#define SERVOMIN          150                             // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX          600                             // This is the 'maximum' pulse length count (out of 4096)
#define USMIN             600                             // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX             2400                            // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ        50                              // Analog servos run at ~50 Hz updates

// ----- CONTROL SETTINGS -----
#define DATA_LEN_MAX      18

#define CMD_FAN_TEST      1
#define CMD_SERVO_BOTTOM  2
#define CMD_SERVO_LEFT    3
#define CMD_SERVO_RIGHT   4

#define CMD_MASK          0x80

// ----- SERVO VARIABLES -----
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
uint16_t pulse_length = 0;

// ----- FAN MEASUREMENT VARIABLES -----
unsigned long previous_ms = 0;                      // ??
unsigned long current_ms = 0;                       // ??
unsigned long timeout = 900;                        // ??

volatile int pulses = 0;

// ----- CONTROL VARIABLES -----
typedef struct {
    unsigned char arduino_number;
    unsigned char cmd;
    unsigned char data_length;
    unsigned char data_buffer[DATA_LEN_MAX];
} CMD_t;

CMD_t tx_data;
CMD_t rx_data;

// Initialize the Ethernet server library
EthernetServer server(PORT);

// Initializes wiznet ethernet chip
void init_wiznet_chip() {
  // Set control pins
  pinMode(SS, OUTPUT);
  pinMode(RST, OUTPUT);
  digitalWrite(SS, LOW);

  // Reset chip
  digitalWrite(RST, LOW);
  delay(200);
  digitalWrite(RST, HIGH);

  // Wait for chip to start
  delay(200);
}

// Initializes ethernet connection
void init_ethernet_connection() {
  Serial.println("Trying to get an IP address using DHCP");

  // Check if ethernet cable is connected
  while (Ethernet.linkStatus() == LinkOFF)
  {
    Serial.println("Ethernet cable is not connected.");
    delay(000);
  }

  // Check if DHCP is enabled
  if(DHCP) {
    // Try go get IP via DHCP
    if(Ethernet.begin(mac) == 0)
    {
      Serial.println("Failed to configure Ethernet using DHCP");

      // Check wiznet chip
      if(Ethernet.hardwareStatus() == EthernetNoHardware) {
        Serial.println("WIZnet chip not found!");
        while(true) {}
      }

      // Fallback to static IP
      Ethernet.begin(mac, ip);
    }
  }
  else {
    // Set static IP
    Ethernet.begin(mac, ip);
  }
}

// Initializes fan measurement pins
void init_fan_measurement() {
  // Set pins to input
  pinMode(ENCODER0_PIN, INPUT);
  pinMode(ENCODER1_PIN, INPUT);
}

// Interrupt for fan measurement
// called when encoder 0 pin rises
void FrequencyCounterIRQ0() {
  // Check if encoder pin 1 is enabled
  if (digitalRead(ENCODER1_PIN)) {
    // Fan is in positive direction
    pulses++;
  }
  //else {
    // Fan is in negative direction
    //pulses--;
  //}
}

// Initializes servo shield
void init_servo_shield() {
  // Initialize PWM
  pwm.begin();

    /*
   * In theory the internal oscillator (clock) is 25MHz but it really isn'tpw
   * that precise. You can 'calibrate' this by tweaking this number until
   * you get the PWM update frequency you're expecting!
   * The int.osc. for the PCA9685 chip is a range between about 23-27MHz and
   * is used for calculating things like writeMicroseconds()
   * Analog servos run at ~50 Hz updates, It is importaint to use an
   * oscilloscope in setting the int.osc frequency for the I2C PCA9685 chip.
   * 1) Attach the oscilloscope to one of the PWM signal pins and ground on
   *    the I2C PCA9685 chip you are setting the value for.
   * 2) Adjust setOscillatorFrequency() until the PWM update frequency is the
   *    expected value (50Hz for most ESCs)
   * Setting the value here is specific to each individual I2C PCA9685 chip and
   * affects the calculations for the PWM update frequency. 
   * Failure to correctly set the int.osc value will cause unexpected PWM results
   */
  pwm.setOscillatorFrequency(27000000);
  pwm.setPWMFreq(SERVO_FREQ);  // Analog servos run at ~50 Hz updates

  // Set default values for servos
  pwm.setPWM(0, 0, 230); // bottom
  pwm.setPWM(1, 0, 250); // left
  pwm.setPWM(2, 0, 250); // right
}

// Calculates modbus crc16
uint16_t crc16_modbus(uint8_t *data, uint16_t length) {
  uint16_t crc = 0xFFFF;
  uint16_t i, j;

  for (i = 0; i < length; i++)
  {
    crc ^= (uint16_t)data[i]; // XOR byte into least sig. byte of crc
    for (j = 0; j < 8; j++) // Loop over each bit
    {
      if (crc & 0x0001) // If the LSB is set
      {
        crc >>= 1; // Shift right and XOR 0xA001
        crc ^= 0xA001;
      }
      else // Else LSB is not set
      {
        crc >>= 1; // Just shift right
      }
    }
  }
  return crc;
}

void send_data(EthernetClient* client, byte len) {
  // Build data packet
  tx_data.arduino_number = ARDUINO_NR;
  tx_data.cmd = rx_data.cmd | CMD_MASK;
  tx_data.data_length = len;

  tx_data.data_buffer[len] = (unsigned char) (crc16_modbus((uint8_t*) &tx_data, 3 + tx_data.data_buffer)%256);
  tx_data.data_buffer[len + 1] = (unsigned char)  (crc16_modbus((uint8_t*) &tx_data, 3 + tx_data.data_buffer)/256);

  // Sende daten
  client->write((uint8_t*) &tx_data, len + 5);
}

bool recv_data(EthernetClient* client) {
  // Check if data is available
  if (client->available() >= 3) {
    // Read data
    rx_data.arduino_number = client->read();
    rx_data.cmd = client->read();
    rx_data.data_length = client->read();
    // Update the previous time
    previous_ms = millis();

    while(client->available() < rx_data.data_length + 2) {
      current_ms = millis();
      if (current_ms - previous_ms >= timeout) {
        client->flush();
        return false;
      }
      
      delay(5);
    }

    // Read remaining data
    client->readBytes(rx_data.data_buffer, rx_data.data_length);

    // Combine the received CRC bytes
    uint16_t crc = client->read() + (client->read() << 8);
    // Calculate CRC
    uint16_t crc_check = crc16_modbus((uint8_t*) &rx_data, 3 + rx_data.data_length);

    client->flush();

    // Check data validity
    if (rx_data.arduino_number == ARDUINO_NR && crc == crc_check) {
      return true;
    }

    return false;
  }

  return false;
}

void parse_data(EthernetClient* client) {
  Serial.println(rx_data.cmd);
  switch(rx_data.cmd) {
    case CMD_FAN_TEST:
      // Reset fan counter
      pulses = 0;
      // Enable measurement interrupt
      attachInterrupt(digitalPinToInterrupt(ENCODER0_PIN), FrequencyCounterIRQ0, RISING);
      // Wait for measurement
      delay(500);
      // Disable measurement interrupt
      detachInterrupt(ENCODER0_PIN);
      // Calculate RPM
      pulses = pulses * 24;  // *2 500ms * 60 per minute / 5 blades 
      // Write data to send buffer
      memcpy(tx_data.data_buffer, &pulses, sizeof(int));
      // Send data
      send_data(client, sizeof(int));
      break;
    case CMD_SERVO_BOTTOM:
        memcpy(&pulse_length, &rx_data.data_buffer[0], 2);
        pwm.setPWM(0, 0, pulse_length);
        Serial.print("BOTTOM:"); Serial.println(pulse_length);
        memcpy(tx_data.data_buffer, &rx_data.data_buffer[0], 2);

        send_data(client, 2);
        break;
    case CMD_SERVO_LEFT:
      memcpy(&pulse_length, &rx_data.data_buffer[0], 2);
      pwm.setPWM(1, 0, pulse_length);
      Serial.print("BOTTOM:"); Serial.println(pulse_length);
      memcpy(tx_data.data_buffer, &rx_data.data_buffer[0], 2);

      send_data(client, 2);
      break;
    case CMD_SERVO_RIGHT:
      memcpy(&pulse_length, &rx_data.data_buffer[0], 2);
      pwm.setPWM(2, 0, pulse_length);
      Serial.print("BOTTOM:"); Serial.println(pulse_length);
      memcpy(tx_data.data_buffer, &rx_data.data_buffer[0], 2);

      send_data(client, 2);
      break;
    default: 
      break;
  }
}

void setup() {
  init_servo_shield();
  init_fan_measurement();

  init_wiznet_chip();

  // Open serial connection
  Serial.begin(9600);

  // Init ethernet connection
  init_ethernet_connection();

  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
}

void loop() {
  // if an incoming client connects, there will be bytes available to read:
  EthernetClient client = server.available();
  if (client) {
    // Check if data was received
    if (recv_data(&client)) {
      // Parse data
      parse_data(&client);
    }
  }
}

When using only the ethernet shield, I'm getting an IP address from the DHCP server but renewing DHCP lease is not working, no idea how to implement this.

Sincerely,
Tropaios

Please post an annotated schematic showing how you’ve wired everything, including links to the technical documentation for each hardware device. Note that links to marketplace vendors typically only provide sales information, which may not be sufficient for troubleshooting.

@gilshultz Okay, no problem.
Schematic for the Ethernet Shield: https://dfimg.dfrobot.com/62b2fb5caa613609f271523c/wiki/d28633d4c8d32fd619b76f9da9aea385.pdf
For the servo shield there sadly is no schematic, only a datasheet.
But the shield is based on the adafruit servo shield, which uses the same i2c chip "PCA9685"
There isn't really a wiring, only a POE ethernet cable and a 5V supply for the servos. I'm sure the wiring is correct, since the shields work fine when used separately.

The DFRobot wiki mentions using this PoE board with a Leonardo specifically. You mention having a Uno (R3).

The Leonardo I2C pins aren't the same as the Uno's.
Are you trying to pick off I2C from the pins on the PoE board?

Ethernet & PoE Shield for Arduino-W5500 Wiki - DFRobot

As far as I understand they just use an Arduino Leonardo for this example.
On the description site of this shield it mentions the Uno and says "Functionally compatible with [Arduino Ethernet Shield 2" and the Arduino Ethernet Shield 2 supports the UNO and MEGA.

If it would really only work with the Leonardo then the boards each on its own wouldn't work either I think. I tested both boards separately with the Arduino Uno.

I managed to get the boards to work together ones and they worked for a short while. Afterwards the Ethernet Shield lost connection.

I have no idea why, but when the Servo Shield is added on top of the Ethernet Shield, the Ethernet Shield cannot connect to my router, it always fails. When I remove the Servo Shield it connects to my router but if add the Servo Shield again, the connection is lost, as if the Ethernet Board is resetted.

What if the servo board is in the middle of the stack ?

If I'd gone to the very end --

Won't work since the Ethernet Shield needs the SPI connector.

The PoE board runs with the sketch posted - if the servo board is not mounted ?

Yes it does. Both board work if used alone.

Check your code and check your shields to see if each uses the same pinout somewhere.

Tom.... :smiley: :+1: :coffee: :australia: