Skip to content

Commit 18b485a

Browse files
authored
Improve I2C receive retry logic (#114)
* chore: implement retry logic on i2c receive * test: conform to new implementation
1 parent 3d482e9 commit 18b485a

8 files changed

+108
-90
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*.gcov
77
vgcore.*
88
*.tests
9+
failed_test_run
910

1011
.vscode/
1112
build/

src/NoteI2c_Arduino.cpp

+49-40
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,23 @@ NoteI2c_Arduino::receive (
3939
)
4040
{
4141
const char *result = nullptr;
42-
uint8_t transmission_error = 0;
4342

44-
// Request response data from Notecard
45-
for (size_t i = 0 ; i < 3 ; ++i) {
43+
const size_t retry_count = 3;
44+
size_t i = 0;
45+
do {
46+
uint8_t transmission_error = 0;
47+
48+
// Request response data from Notecard
4649
_i2cPort.beginTransmission(static_cast<uint8_t>(device_address_));
4750
_i2cPort.write(static_cast<uint8_t>(0));
4851
_i2cPort.write(static_cast<uint8_t>(requested_byte_count_));
4952
transmission_error = _i2cPort.endTransmission();
5053

51-
// Break out of loop on success
52-
if (!transmission_error) {
53-
break;
54-
}
55-
5654
switch (transmission_error) {
55+
case 0:
56+
// I2C transmission was successful
57+
result = nullptr;
58+
break;
5759
case 1:
5860
result = ERRSTR("i2c: data too long to fit in transmit buffer {io}",i2cerr);
5961
break;
@@ -72,42 +74,49 @@ NoteI2c_Arduino::receive (
7274
default:
7375
result = ERRSTR("i2c: unknown error encounter during I2C transmission {io}",i2cerr);
7476
}
75-
}
76-
77-
// Delay briefly ensuring that the Notecard can
78-
// deliver the data in real-time to the I2C ISR
79-
::delay(2);
80-
81-
// Read and cache response from Notecard
82-
if (!transmission_error) {
83-
const int request_length = requested_byte_count_ + NoteI2c::REQUEST_HEADER_SIZE;
84-
const int response_length = _i2cPort.requestFrom((int)device_address_, request_length);
85-
if (!response_length) {
86-
result = ERRSTR("serial-over-i2c: no response to read request {io}",i2cerr);
87-
} else if (response_length != request_length) {
88-
result = ERRSTR("serial-over-i2c: unexpected raw byte count {io}",i2cerr);
89-
} else {
90-
// Ensure available byte count is within expected range
91-
static const size_t AVAILBLE_MAX = (NoteI2c::REQUEST_MAX_SIZE - NoteI2c::REQUEST_HEADER_SIZE);
92-
uint32_t available = _i2cPort.read();
93-
if (available > AVAILBLE_MAX) {
94-
result = ERRSTR("serial-over-i2c: available byte count greater than max allowed {io}",i2cerr);
95-
}
96-
// Ensure protocol response length matches size request
97-
else if (requested_byte_count_ != static_cast<uint8_t>(_i2cPort.read())) {
98-
result = ERRSTR("serial-over-i2c: unexpected protocol byte count {io}",i2cerr);
99-
}
100-
// Update available with remaining bytes
101-
else {
102-
*available_ = available;
10377

104-
for (size_t i = 0 ; i < requested_byte_count_ ; ++i) {
105-
//TODO: Perf test against indexed buffer writes
106-
*buffer_++ = _i2cPort.read();
78+
// Read and cache response from Notecard
79+
if (!transmission_error) {
80+
// Delay briefly ensuring that the Notecard can
81+
// deliver the data in real-time to the I2C ISR
82+
::delay(2);
83+
84+
const int request_length = requested_byte_count_ + NoteI2c::REQUEST_HEADER_SIZE;
85+
const int response_length = _i2cPort.requestFrom((int)device_address_, request_length);
86+
if (!response_length) {
87+
result = ERRSTR("serial-over-i2c: no response to read request {io}",i2cerr);
88+
} else if (response_length != request_length) {
89+
result = ERRSTR("serial-over-i2c: unexpected raw byte count {io}",i2cerr);
90+
} else {
91+
// Ensure available byte count is within expected range
92+
static const size_t AVAILABLE_MAX = (NoteI2c::REQUEST_MAX_SIZE - NoteI2c::REQUEST_HEADER_SIZE);
93+
uint32_t available = _i2cPort.read();
94+
if (available > AVAILABLE_MAX) {
95+
result = ERRSTR("serial-over-i2c: available byte count greater than max allowed {io}",i2cerr);
96+
} else if (requested_byte_count_ != static_cast<uint8_t>(_i2cPort.read())) {
97+
// Ensure protocol response length matches size request
98+
result = ERRSTR("serial-over-i2c: unexpected protocol byte count {io}",i2cerr);
99+
} else {
100+
// Update available with remaining bytes
101+
*available_ = available;
102+
103+
for (size_t i = 0 ; i < requested_byte_count_ ; ++i) {
104+
//TODO: Perf test against indexed buffer reads
105+
*buffer_++ = _i2cPort.read();
106+
}
107+
result = nullptr;
108+
break;
107109
}
108110
}
109111
}
110-
}
112+
113+
// Flash stalls have been observed on the Notecard ESP. Delaying
114+
// between retries provides time for the Notecard to recover from
115+
// the resource contention.
116+
::delay(1000);
117+
NOTE_C_LOG_ERROR(result);
118+
NOTE_C_LOG_WARN("serial-over-i2c: reattempting to read Notecard response");
119+
} while (result && (i++ < retry_count));
111120

112121
return result;
113122
}

0 commit comments

Comments
 (0)