I'm using an Arduino Uno with Ethernet Shield.
After sending many HTTP requests, client.println(...), the client starts to fail when connecting. The time to failure appears to be random, and the sequence readout from the loop can vary anywhere between ~1000 and ~7000.
The error is not to do with the Ethernet Transmit Buffer overflowing (Following this advice)
Here is the code that is failing:
#include <Ethernet.h>
#include <SPI.h>
// Network constants
byte mac[] = {0x00, 0x23, 0xdf, 0x82, 0xd4, 0x01};
byte ip[] = {/*REDACTED*/};
byte server[] = {/*REDACTED*/};
int port = /*REDACTED*/;
Client client(server, port);
// State
int sequence;
void setup(){
Ethernet.begin(mac, ip);
Serial.begin(9600);
sequence = 0;
delay(1000);
}
void loop(){
httpPut("/topic/:test/publish?sessionId=SESenanhygrp");
Serial.println(sequence++);
}
void httpPut(char* url){
if (!client.connect()) {
Serial.println("EXCEPTION: during HTTP PUT. Could not connect");
return;
}
client.print("PUT");
client.print(" ");
client.print(url);
client.println(" HTTP/1.0");
client.println();
while(!client.available()){
delay(1);
}
while(client.available()) {
char c = client.read();
Serial.print(c);
}
while(client.connected()){
Serial.println("Waiting for server to disconnect");
}
client.stop();
}
The error occurs in the following segment
if (!client.connect()) {
Serial.println("EXCEPTION: during HTTP PUT. Could not connect");
return;
}
There is a bug in the Arduino Ethernet library in v22 (as discussed in Linux/Windows V0022/1.0 Ethernet problem SOLVED).
The solution for me was to use the Ethernet2 library (by Peter from tinker.it). The code needed minor tinkering, but everything appears to be working fine now. I've managed to get over 40000+ HTTP messages sent without any problems. (Occasionally single messages cannot be sent, but this error rate is less than 4%.)
I would slow down the communication rate by increasing time 10x between the messages. Then if you don't get an error between 1000 and 7000 messages, it would probably mean that you are talking too fast to your little Arduino and it's buffer gets an overflow which communication library unfortunately can not recover from. I would also monitor Arduino free bytes in a buffer over serial port after each message. You can also test for this behavior by sending messages as fast as you can from PC, and see if that will freeze your Arduino after a while. If it does, you might consider to deny messages until buffer is above some limit.
Related
Please help! I am using FSMC to connect a STM32F407 MCU with AD7606 to sample voltage value. MCU would send sampled values to PC using USB HS port after 1024 conversions. But when I inspect the values from PC, I found that readings from channel 0 occasionally contains data from other channels. For example, if connect channel 0 to 5v, connect channel 8 to 3.3v, connect other channels to ground. Then the printed value from channel 0 would contain 5v, 0v, 3.3v. The basic setup is as follows:
A 200KHZ PWM single is generated by TIM10 to act as CONVST signal for AD7606.
7606 will then issue a BUSY signal which I used as an external interrupt source.
In the Interrupt handler, An DMA request would be issued to read 8 16bit data
from FSMC address space to memory space. TIM10 PWM would be stopped if 1024
conversions has been done.
In the DMA XFER_CPLT call back, if 1024 conversions has been done, the converted
data would be sent out by USB HS port, and TIM10 PWM would be enabled again.
Some code blocks:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_7)
{
// DMA data from FSMC to memory
HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream0, 0x6C000000, (uint32_t)(adc_data + adc_data_idx) , 8);
adc_data_idx += 8;
if (adc_data_idx >= ADC_DATA_SIZE)
HAL_TIM_PWM_Stop(&htim10, TIM_CHANNEL_1);
}
}
void dma_done(DMA_HandleTypeDef *_hdma)
{
int i;
int ret;
// adc_data[adc_data_idx] would always contain data from
// channel 1, led1 wouldn't light if every thing is fine.
if (adc_data[adc_data_idx] < 0x7f00 )
HAL_GPIO_WritePin(led1_GPIO_Port, led1_Pin, GPIO_PIN_SET);
if (adc_data_idx >= ADC_DATA_SIZE)
{
if(hUsbDeviceHS.dev_state == USBD_STATE_CONFIGURED)
{
// if I don't call CDC_Transmit_HS, everything is fine.
ret = CDC_Transmit_HS((uint8_t *)(adc_data), ADC_DATA_SIZE * 2 );
if (ret != USBD_OK)
{
HAL_GPIO_WritePin(led1_GPIO_Port, led2_Pin, GPIO_PIN_SET);
}
}
adc_data_idx = 0;
HAL_TIM_PWM_Start(&htim10, TIM_CHANNEL_1);
}
}
It seems that a single USB transaction would take longer than 5us(one conversion time), so I stopped PWM signal to stop conversion...
If I only send the second half of the data buffer, there is no data mixture. It's very strange.
According to your description, I think the processing is correct, and the problem is at the CDC_Transmit_HS(); I have met the problem on the CDC_Transmit_FS(), which can't transmit more than 64 bytes data for original code, and need to modify some code, otherwise the some error occurs. Did you check the number of received data is correct?
Reference:
I can't receive more than 64 bytes on custom USB CDC class based STM32 device
I'm not sure your ADC_DATA_SIZE size; if it's larger than 64 bytes, maybe you can modify to smaller than 64 bytes and try again and check whether or not the data is correct. I am not sure if it is affected by this problem, but I think you can give it a try.
On the other hand, it may also be necessary to GND the ADC IN pins not used by AD7606 to avoid interference between channels.
Or you can try other communication (I2C, SPI, UART...etc) to send the data.
If there is no problem with other communication methods, there is a high chance that it is a problem with CDC_Transmit_HS(). If there are problems with other transmission methods, you may have to check whether there is a conflict between the ADC conversion time or the transmission time.
I have an ESP8266-01 and I want to send sensor data from Arduino to ESP8266 via serial communication but the data I receive is not correct, I tried changing baud rates but no luck, here is the code
Code for ESP8266:
#include <SoftwareSerial.h>
void setup() {
// put your setup code here, to run once:
Serial.begin(38400);
}
void loop() {
// put your main code here, to run repeatedly:
while(Serial.available()) {
Serial.println("yes");
Serial.println(Serial.read());
}
delay(5);
}
Code for Arduino
#include <SoftwareSerial.h>
SoftwareSerial esp(1, 0);
String str;
void setup(){
Serial.begin(38400);
esp.begin(38400);
delay(2000);
}
void loop() {
// put your main code here, to run repeatedly:
str = String("Hi there");
esp.println(str);
esp.println("hi there");
delay(1000);
}
Here is the serial monitor:
Serial monitor showing numbers instead of "hi there"
The wiring connections are correct.
if you are using simple wires for the connection you might need to reduce the baud rate at 9600 or lower.
For higher speed you will need coaxial cables to get a decent communication distance. Moreover I am sure that you have checked the electrical connection obvously.
On the programming side, Serial.read() returns the first byte of incoming serial data available (or -1 if no data is available). Data type: int. This is why you are getting a single number instead of your string.
I would suggest to use Serial.readString() instead.
Please see the proper documentation here:
https://www.arduino.cc/reference/en/language/functions/communication/serial/
All the best
Fairly new to the Atmel software framework, usually use MPLab/PIC devices but for my project I needed to build a UART router capable of connecting 6 UART devices together so I chose a SAMD20J16 chip. So far iv'e configured the drivers using ATMEL start with USART HAL_ASYNC drivers for all USART modules.
Iv'e gotten the device to write to all 6 USART modules and have gotten the read interrupt to trigger but I am unable to read from the usart buffer. For now ive stripped it down to the minimal code possible to try and find out why it's not working, it's probably a simple oversight on my part, but Iv'e been searching the internet for 2 days and all the documentation I can find using the HAL_ASYNC drivers in ASF4 is flaky at best.
#include <atmel_start.h>
int main(void)
{
int nRead=0;
uint8_t rxPData;
/* Initializes MCU, drivers and middleware */
atmel_start_init();
//DECLARE UART STRUCTURTES
struct io_descriptor *uartD1;
usart_async_get_io_descriptor(&USART_0, &uartD1);
usart_async_enable(&USART_0);
struct io_descriptor *uartP;
usart_async_get_io_descriptor(&USART_1, &uartP);
usart_async_enable(&USART_1);
struct io_descriptor *uartD3;
usart_async_get_io_descriptor(&USART_2, &uartD3);
usart_async_enable(&USART_2);
struct io_descriptor *uartD4;
usart_async_get_io_descriptor(&USART_3, &uartD4);
usart_async_enable(&USART_3);
struct io_descriptor *uartS;
usart_async_get_io_descriptor(&USART_4, &uartS);
usart_async_enable(&USART_4);
struct io_descriptor *uartD2;io_write(uartP, (uint8_t *) "UART P\r\n", 8);
usart_async_get_io_descriptor(&USART_5, &uartD2);
usart_async_enable(&USART_5);
void uartP_callback(const struct usart_async_descriptor *const io_descriptor){
if(usart_async_is_rx_not_empty(uartP)){
io_write(uartP, (uint8_t *) "TEST P\r\n", 8);
nRead=io_read(uartP,&rxPData,6);
io_write(uartP,&rxPData,nRead);
}
}
usart_async_register_callback(&USART_1, USART_ASYNC_RXC_CB, uartP_callback);
while (1) {
}
}
Right now the observed behavior is anytime I send a UART packet it triggers the interrupt, and passes the IF rx_buffer_is_not_empty and sends back "TEST P\r\n". I added this line of code after some testing just to see if the interrupt was triggering.
However, no matter what input size i send to it it never transmits back any data that was sent to it.
Right now i'm simply trying to verify that I can receive and transmit on all usart ports, the final code won't have transmits inside the receive interrupts.
I'm facing a weird issue. I've always used bit bangin I2C functions on my PIC16F1459, but now I want to use the MSSP (SPI,I2C Master Slave Peripheral). So I've started writing the functions according to the datasheet, Start, Stop, etc. The problem I have is my PIC won't ACK the data I send to the I2C EEPROM. It clearly says in the datasheet that the ACK status can be found at SSPCON2.ACKSTAT. So my guess was to poll this bit until the slave responds to my data, but the program hangs in the while Loop.
void vReadACK (void)
{
while (SSPCON2.ACKSTAT != 0);
}
And here's my write function, my I2CCheck function and I2C Master Initialization function
void vI2CEcrireOctet (UC ucData, UC ucRW)
{
vI2CCheck();
switch (ucRW)
{
case READ:
SSPBUF = ucData + 1;
break;
case WRITE:
SSPBUF = ucData + 0;
break;
}
vReadACK();
}
void vI2CCheck (void)
{
while (SSPCON2.ACKEN); //ACKEN not cleared, wait
while (SSPCON2.RCEN); //RCEN not cleared, wait
while (SSPCON2.PEN); //STOP not cleared, wait
while (SSPCON2.SEN); //Start not cleared, wait
while (SSPCON2.RSEN); //Rep start not cleared, wait
while (SSP1STAT.R_NOT_W); //TX not done wait
}
void vInitI2CMaster (void)
{
TRISB4_bit = 1; //SDA IN
TRISB6_bit = 1; //SCL IN
SSP1STAT.SMP = 1; //No slew rate
SSP1STAT.CKE = 0; //Disable SMBus inputs
SSPADD = 0x27; //100 KHz
SSPCON1 = 0b00101000; //I2C Master mode
SSPCON3 = 0b00000000; //Rien de slave
}
Just so you know, 24LC32A WriteProtect tied to VSS, A2-A1-A0 tied to GND, so adress 0xA0. 4k7 pull-ups are on I2C line. PIC16F1459 at 16MHz INTOSC.
I'm completely stuck. I've went through the MSSP datasheet 5 to 6 times without finding any issue. Can you guys help?
And here's my logic analyzer preview (removing the while inside vReadAck() )
Well it looks like I've found the answer to my question. What I was doing was the exact way of doing this. The problem seemed to be the Bus Free Time delay required for the slave to respond. At 16Mhz, my I2C was probably too fast for the EEPROM memory. So I've added a small Delay function right after the stop operation, so the write sequences are delayed and BAM, worked.
Bus free time: Time the bus
must be free before a new
transmission can start.
Despite the fact you "totally know" know "PIC won't ACK the data I send to the I2C EEPROM" because it's not supposed to, you still seem to misunderstand how I2C acknowledgements are supposed to work. They're called acknowledgements because they can be both positively (ACK) and negatively (NAK) acknowledged. If you look at the the analyzer screen shot you posted you'll find that its quite clearly labelled each byte being sent as having been NAK'ed by the transmitter.
To properly check for I2C ACKs you should be polling the trailing edge of the ACKTIM bit, and then checking the ACKSTAT bit to find out whether the slave transmitted an ACK or a NAK bit. Something like this:
int
vReadACK() {
while(!SSPCON3.ACKTIM);
while(SSPCON3.ACKTIM);
return SSPCON2.ACKSTAT;
}
As for why your slaved device is apparently NAKing each byte it isn't clear from the code you've posted, but there's a couple of notable omissions from your code. You need to generate start and stop conditions but you've shown no code to do this.
When i use Ethernet.begin(mac,ip) then the led is not turning on and off. But when i not use that line it works. But i need to have led on and off by using that Ethernet and UPP modules. How can i ?
Board model: Ethernet 08
Twinker kit, Twinker relay
#include <TinkerKit.h>
#include <SPI.h> // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h> // UDP library from: bjoern#cs.stanford.edu 12/30/2008
TKLed led(O5); //O0 does not work
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 177);
unsigned int localPort = 8888; // local port to listen on
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
char ReplyBuffer[] = "ackv1"; // a string to send back
EthernetUDP Udp;
void setup() {
Ethernet.begin(mac,ip);
Udp.begin(localPort);
}
void loop() {
int packetSize = Udp.parsePacket();
if(packetSize)
{
// check the switch state
delay(1000);
led.off();
delay(3000);
led.on();
// send a reply, to the IP address and port that sent us the packet we received
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(ReplyBuffer);
Udp.endPacket();
}
delay(10);
}
EDIT:
$ echo "hello" | nc -4u 192.168.1.177 8888
ackv1
From your description it sounds like the Ethernet I/O is interfering with the LED I/O.
Notice that:
TKLed led(O0);
This probably defines the led to be at port 0. You need to determine what ports are used by Ethernet. (I would tell you but this is a good lesson.)
IF (you need to read documentation) there is a conflict between the led port number and the ethernet port numbers then you will need to move the LED, because the ethernet ports require serial I/O, while the LED just requires a +5V or 0V.