I'm writing a program which reads MPU9250's accelerometer&gyro data using MPU9250's internal FIFO and serves web interface.
Without web access, everything is fine.
However, on the exact timing of web request, bytes read on that timing is changed.
My current code just simply shows template webpage and prints IMU accelerometer value only if its magnitude is greater than 2.5. So, if I do not touch MPU9250 sensor, its value should be between 0.9~1.1. However, it prints some wrong values greater than 2.5 when the webpage is refreshed (currently auto-refreshing in 0.5 seconds ) although MPU9250 sensor is not touched at all.
I'm using LOLIN D32 PRO board. and MPU9250 is connected its default VSPI bus.
I read MPU 9250's FIFO data on arduino's loop function as follows,
while( readBytes = spiread_fifo( buffer + readBytes_all / 2 ) )
readBytes_all += readBytes;
if ( readBytes_all == 0 )
return;
if( digitalRead( 4 ) == 1 ){
int x = buffer[ 0 ];
int y = buffer[ 1 ];
int z = buffer[ 2 ];
double m = sqrt( x * x + y * y + z * z ) / 2048;
if( m > 2.5 )
Serial.println( m );
and spiread_fifo is defined as follows
int spiread_fifo( volatile short * buffer ) {
int fifo_len = spiread( 0x72 ) * 256 + spiread( 0x73 );
if ( fifo_len > 512 )
return -1;
if ( fifo_len == 0 )
return 0;
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x74 | 0x80 );
MPU9250.transfer( 0x00 ); // if I use SPI CLOCK more than 8~12Mhz, it gives me duplicated byte at the beginning. So just drop one of them.
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
for ( int i = 3; i < fifo_len / 2; i++ )
MPU9250.transfer16( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
return fifo_len;
}
If I do not touch or exert any force on MPU9250, serial console should be silent and it actually does if there's no ESP32's webserber access. However it gives some random value on the timing of webpage access, like follows. ( single value per single webpage access )
15.53
16.11
15.60
13.59
16.86
2.55
2.55
3.85
3.85
3.37
6.79
2.63
2.56
5.80
10.18
5.88
3.65
5.80
5.48
2.95
4.01
4.01
3.10
2.90
3.17
9.31
14.97
7.08
16.29
which are totally abnormal values.
My guesses are,
- strong RF signal affects SPI bus so signals are altered.
- wifi TX routine is called during MPU9250 FIFO readout process and it causes data drop during FIFO read.
Whatever the reason is, I don't know how to fix the problem.
Any possible causes/solutions would be appreciated.
Belows are my current wiring. However, nothing's special. MPU9250 is connected to default VSPI port and INT pin is connected to GPIO34. other remaining connection is not used.
full source codes below for your reference.
MAIN CODE :
extern volatile int cnt;
volatile short* buffer;
volatile short* buffer2;
volatile unsigned long *timestamp;
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
void printHex( int num, int precision) {
char tmp[16];
char format[128];
sprintf(format, "%%.%dX ", precision);
sprintf(tmp, format, num);
if ( strlen( tmp ) > precision + 1 ) {
int l = strlen( tmp ) - precision - 1;
for ( int i = 0; i < precision + 2; i++ ) {
tmp[ i ] = tmp[ i + l ];
}
}
Serial.print(tmp);
}
void setup() {
Serial.begin( 2000000 );
Serial.println( "Turning on...." );
buffer = ( short * )ps_malloc( 1000000 );
buffer2 = ( short * )ps_malloc( 1000000 );
BUTTONSetup();
MPU9250Setup();
OTASetup();
WEBSERVERSetup();
Serial.println( "Setup finished." );
}
void loop() {
OTAHandle();
WEBSERVERHandle();
int readBytes = 0, readBytes_all = 0;
while( readBytes = spiread_fifo( buffer + readBytes_all / 2 ) )
readBytes_all += readBytes;
if ( readBytes_all == 0 )
return;
if( digitalRead( 4 ) == 1 ){
int x = buffer[ 0 ];
int y = buffer[ 1 ];
int z = buffer[ 2 ];
double m = sqrt( x * x + y * y + z * z ) / 2048;
if( m > 2.5 )
Serial.println( m );
BUTTONHandle();
}
}
MPU9250 CODE :
#include <SPI.h>
#define SCK 18
#define MISO 19
#define MOSI 23
#define SS 5
#define INT 34
SPIClass MPU9250( VSPI );
SPISettings settingsA( 1000000, MSBFIRST, SPI_MODE3 );
SPISettings settingsB( 20000000, MSBFIRST, SPI_MODE3 );
volatile int cnt = 0;
void IRAM_ATTR onInterrupt() {
portENTER_CRITICAL_ISR(&mux);
cnt++;
portEXIT_CRITICAL_ISR(&mux);
}
void spiwrite( byte a, byte b ) {
MPU9250.beginTransaction( settingsA );
digitalWrite( SS, LOW );
MPU9250.transfer( a );
MPU9250.transfer( b );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
}
byte spiread( byte a ) {
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( a | 0x80 );
byte r = MPU9250.transfer( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
return r;
}
int spiread_fifo( volatile short * buffer ) {
int fifo_len = spiread( 0x72 ) * 256 + spiread( 0x73 );
// fifo_len += 12;
// fifo_len = fifo_len / 12 * 12;
if ( fifo_len > 512 )
return -1;
if ( fifo_len == 0 )
return 0;
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x74 | 0x80 );
MPU9250.transfer( 0x00 ); // if I use SPI CLOCK more than 8~12Mhz, it gives me duplicated byte at the beginning. So just drop one of them.
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
for ( int i = 3; i < fifo_len / 2; i++ )
MPU9250.transfer16( 0x00 );
// for( int i = fifo_len / 2 + 1; i < fifo_len; i++ )
// buffer[ i ] = 0;
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
return fifo_len;
}
void spiread_raw( volatile short * buffer ) {
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x3b | 0x80 );
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
}
void spiread_raw_gyr( volatile short * buffer ) {
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x43 | 0x80 );
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
}
void spiread_raw_accgyr( volatile short * buffer ) {
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x3b | 0x80 );
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
MPU9250.transfer16( 0x00 );
for ( int i = 3; i < 6; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
}
void MPU9250Setup(){
pinMode( SS, OUTPUT );
pinMode( SCK, OUTPUT );
pinMode( MOSI, OUTPUT );
pinMode( INT, INPUT_PULLUP );
pinMode( MISO, INPUT );
pinMode( 4, INPUT );
MPU9250.begin( SCK, MISO, MOSI, SS ); //CLK,MISO,MOIS,SS
attachInterrupt( digitalPinToInterrupt( INT ), onInterrupt, FALLING );
spiwrite( 0x68, 0x07 );
spiwrite( 0x6A, 0x55 ); // FIFO_EN = 1, FIFO_RST = 1;
spiwrite( 0x19, 0x00 ); // SMPLRT_DIV = 0
spiwrite( 0x1B, 0x18 ); // GYRO_FS_SEL = 3, Fchoice_b = 0
spiwrite( 0x1C, 0x18 ); // ACCEL_FS_SEL = 3
spiwrite( 0x1D, 0x08 ); // accel_fchoice_b = 1
// spiwrite( 0x23, 0x78 ); // TEMP_OUT = 0, GYRO_XOUT = 1, GYRO_YOUT = 1, GYRO_ZOUT = 1, ACCEL = 1
// spiwrite( 0x23, 0x79 ); // TEMP_OUT = 0, GYRO_XOUT = 0, GYRO_YOUT = 0, GYRO_ZOUT = 0, ACCEL = 1
spiwrite( 0x23, 0x08 ); // TEMP_OUT = 0, GYRO_XOUT = 0, GYRO_YOUT = 0, GYRO_ZOUT = 0, ACCEL = 1
spiwrite( 0x37, 0x10 ); // INT_ANYRD_2CLEAR = 1
spiwrite( 0x38, 0xC1 ); // ACTL = 1, OPEN = 1, RAW_RDY_EN = 1
spiwrite( 0x1A, 0x07 ); // FIFO_MODE = 0, EXT_SYNC_SET = 0, DLPF_CFG = 7
}
OTA firmware CODE :
#include <WiFi.h>
#include "esp_wifi.h"
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
const char* ssid = "XXXXXX";
const char* password = "XXXXXX";
void OTASetup(){
delay( 100 );
esp_wifi_set_max_tx_power( -100 );
WiFi.mode( WIFI_STA );
WiFi.setHostname( "LOLIN_D32_PRO_Sunkyue" );
delay( 100 );
WiFi.begin( ssid, password );
while( WiFi.waitForConnectResult() != WL_CONNECTED ){
Serial.println( "Connection Failed! Rebooting..." );
delay( 10 );
ESP.restart();
}
ArduinoOTA.setPort(53232);
ArduinoOTA.setHostname("LOLIN_D32_PRO_Sunkyue");
ArduinoOTA.setPassword("XXXXXX");
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void OTAHandle(){
ArduinoOTA.handle();
}
WEBSERVER CODE :
#include <WiFiClient.h>
#include <WebServer.h>
WebServer server(80);
void handleRoot() {
char temp[400];
int sec = millis() / 1000;
int min = sec / 60;
int hr = min / 60;
snprintf(temp, 400,
"<html>\
<head>\
<meta http-equiv='refresh' content='0.5'/>\
<title>ESP32 Demo</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1>Hello from ESP32!</h1>\
<p>Uptime: %02d:%02d:%02d</p>\
<img src=\"/test.svg\" />\
</body>\
</html>",
hr, min % 60, sec % 60
);
server.send(200, "text/html", temp);
}
void handleNotFound() {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void drawGraph() {
String out = "";
char temp[100];
out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
out += "<g stroke=\"black\">\n";
int y = rand() % 130;
for (int x = 10; x < 390; x += 10) {
int y2 = rand() % 130;
sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
out += temp;
y = y2;
}
out += "</g>\n</svg>\n";
server.send(200, "image/svg+xml", out);
}
void WEBSERVERSetup(){
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
server.on("/", handleRoot);
server.on("/test.svg", drawGraph);
server.on("/inline", []() {
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
server.begin();
}
void WEBSERVERHandle(){
server.handleClient();
}