0

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,

  1. strong RF signal affects SPI bus so signals are altered.
  2. 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.

PICTURE 1

PICTURE 2

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();
}
  • Self answer. It was just buffer overflow. Since the buffer of MPU9250 is filled in 10ms and server.handleClient takes longer than 10ms, it causes buffer overflow. I think I can solve the issue by using multi core approach but I don't know how to handle this kind of situation if I only have single core... – Sunkyue Kim Apr 30 '19 at 02:40

1 Answers1

0

I can see two solutions for a uni-processor solution(since you have not stated the final goals one may be more optimal than the other):

  1. If it is ok to miss approximately 15-25 ms of data(a tap of the sensor should still register,) Change the spiread_fifo(...) to spiread_raw_accgyr(buffer) in the loop(). This will read the current values into the buffer at the time of the execution. This would definitely not be appropriate for position calculations or calculations of seismic events.

  2. Read and analyze the required data in an interrupt routine(either the hardware pin from the MPU9250, or a timer), since the web client is much more forgiving of delays(within reason).

Either way, the calculations should be optimized to reduce the instruction count(the ESP32 does not have an FPU, so all of the floating point operations must be emulated in software):

The lines:

double m = sqrt( x * x + y * y + z * z ) / 2048;
  if(  m > 2.5 )

Can be simplified (if perfect (spherical) accuracy is required) by algebraically squaring both sides of the equation to:

double m =  (x * x + y * y + z * z)  / 4194304;
  if (m > 6.25)

Or(preferably, but with slightly less accuracy when combining the axes):

double m =  ((abs(x) + abs(y) + abs(z))  / 2048); // Manhattan distance(a diamond in 2D, a four sided, double pyramid in 3d)
if (m > 2.5)

The last line on page 6 of the MPU9250 Datasheet: states:

1MHz SPI serial interface for communicating with all registers

The next line:

20MHz SPI serial interface for reading sensor and interrupt registers

The FIFO register(Page 27 section 4.17 of the datasheet calls the FIFO a register) is not a sensor nor an interrupt register; therefore communications should be limited to 1 MHZ. Any scanning above this rate MAY(and will in the case of out of phase requests) give invalid/inconstant results.

I have not been able to find a reference for the MPU9250 library, or the commands given, to verify this is the case in your code.