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