Myoware Muscle Sensor Not Working? Help!

For some reason I can't get a signal from this sensor, the signal LED does not light up and the serial plotter doesn't respond when I move my muscles. I have tried the electrodes in many positions so I'm certain that's not an issue, and I'm pretty sure my solder joints are good.

The serial plotter gives this consistent response:

My code:

int analogIn = A2;

void setup() 
{
  Serial.begin(9600);
}

void loop() 
{
  float sensorValue = analogRead(analogIn);
  float millivolt = (sensorValue/1023)*5;

  Serial.print("Sensor Value: ");
  Serial.println(sensorValue);

  Serial.println("Voltage: ");
  Serial.print(millivolt*1000);
  Serial.println(" mV");
  Serial.println("");
  delay(1);

}

Please help if you can! I am lost as to what could be wrong

How did you isolate the setup from the USB power? A complete (!) wiring diagram might help to get an idea of your setup.

Take a few images of your setup with good shots of the solder joints and show an image of the mayowear sensor attached to your muscle and connected to the MCU, please.

These are the solder joints:

1 Like

The electrode placement:

1 Like

This is the wiring, only differences are that I am using the Arduino NANO the analog 2 pin instead of 0
Myoware-Muscle-Sensor-Arduino-1-640x275

And the mayo power switch is set to on, correct?

None of the LEDs illuminate on the mayo?

And the sensitivity pot has been messed with?

I asked again: How did you isolate the USB bus? Take a look at the manual to get several ways but without that isolation don't expect to get meaningful results.

The power switch was on, the power LED illuminates. The signal LED occasionally illuminates, however I'm unsure whether it is due to picking signals or due to loose connections

I have not isolated the USB. What does that do?

The pot is at the default 50 kilo-ohms

Not sure what the issue could be your code looks good. I use several mayo's in a project and have not had issue with them.

Here is my base test code for the mayo but it is written for an ESP32.

#include <U8g2lib.h>
#include "freeRTOSSTUFF.h"
#include <driver/adc.h>

////
SemaphoreHandle_t sema_Display;
////
//int dispValues[80] = {0};
//int dispValueCell = 0;
int dispValueCellCount = 125;
////
QueueHandle_t xQ_Display;
////
////
void ULP_BLINK_RUN(uint32_t us);
////
////
const int evtUPDATE_DISPLAY = ( 1 << 0 ); //01
const int evtTakeA_Reading =  ( 1 << 10 ); // 10
////
////
const int SerialDataBits = 115200;
const int DisplayK = 45000;
////
/*
   SH1106, 128X64, 5V or 3.3V ( 3.3V works), no pullups

*/
// U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 5, /* dc=*/ 17, /* reset=*/ 16);
////

/////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
  ULP_BLINK_RUN(100000);
  Serial.begin( SerialDataBits );
  // https://dl.espressif.com/doc/esp-idf/latest/api-reference/peripherals/adc.html
  // set up A:D channels
  adc1_config_width(ADC_WIDTH_12Bit);
  adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);
  //  adc1_config_width(ADC_WIDTH_12Bit);
  //  adc1_config_channel_atten(ADC1_CHANNEL_1, ADC_ATTEN_11db);
  /*
     When VDD_A is 3.3V: 11dB attenuation (ADC_ATTEN_11db) gives full-scale voltage 3.9V
  */
  ////
  // xQ_Display = xQueueCreate( 1, sizeof(dispValues) ); // sends a queue copy of the structure
  xQ_Display = xQueueCreate( dispValueCellCount, sizeof(int) ); // sends a queue copy of the structure
  ////
  if ( u8g2.begin() )
  {
    log_i( "u8gx.begin() all OK" );
  }
  ////
  eg = xEventGroupCreate();
  //// CORE 0
  xTaskCreatePinnedToCore ( fUpdateDisplay, "fUpdateDisplay", DisplayK, NULL, Priority4, NULL, TaskCore0 ); // assigned to core
  sema_Display = xSemaphoreCreateBinary();
  xSemaphoreGive ( sema_Display );
  //// CORE 1
  xTaskCreatePinnedToCore( TaskAnalogRead_MyoWare, "TaskAnalogRead_MyoWare", TaskStack30K, NULL, Priority3, NULL, TaskCore1 ); // assigned to core
  // start the thing
  xEventGroupSetBits( eg, evtTakeA_Reading );
}

void loop() {}

/////////////////////////////////////////////////////
void fUpdateDisplay( void * parameter )
{
  int DispVal;
  u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
  int PositX_From, PositY_From, PositXto;
  for (;;)
  {
    /* wait forever until event bit of task 1 is set */
    xEventGroupWaitBits (eg, evtUPDATE_DISPLAY, pdTRUE, pdTRUE, portMAX_DELAY) ;
    u8g2.clearBuffer(); // clear the internal memory
    //remove the first values from the queue, do not put it back into the queue
    // if ( xQueueReceive( xQ_Display, &DispVal , portMAX_DELAY)  == pdTRUE )
    if ( xQueueReceive( xQ_Display, &DispVal , 0)  == pdTRUE )
    {
      PositX_From = 0; PositY_From = 33 - DispVal; // 
      // log_i( " DisplayVal = %d", DispVal );
      // u8g2.drawStr(0, 64 - DispVal, "."); // write something to the internal memory
      u8g2.drawStr(0, PositY_From, "."); // write something to the internal memory
      for (int j = 1; j <= dispValueCellCount; j++)
      {
        if ( xQueueReceive(xQ_Display, &DispVal , 0)  == pdTRUE )
        {
          PositXto = 33 - DispVal; 
          // u8g2.drawLine( j, PositX1, PositX0, PositY0 );
          u8g2.drawLine( PositX_From, PositY_From, j, PositXto );
          PositX_From = j; PositY_From = PositXto;
          // u8g2.drawVLine( j, DispVal , 1  );
          // u8g2.drawStr( j, 64 - DispVal, "."); // write something to the internal memory
          xQueueSendToBack( xQ_Display, &DispVal, 0 ); // put this value back into the queue
          // log_i( "j=%d DisplayVal = %d", j, DispVal );
          // Serial.print( j );
          // Serial.print( ", " );
          Serial.print( 33 - PositXto );
          // Serial.print( ", " );
          Serial.println();
        }
      } // for (int j = 1; j < dispValueCellCount; j++)
      // u8g2.drawStr(0, 64 - DispVal, "."); // write something to the internal memory
      // u8g2.setCursor( 0, 10 ); u8g2.print( DispVal );
      // u8g2.setCursor( 0, 20 ); u8g2.print( DispVal );
      u8g2.sendBuffer();          // transfer internal memory to the display
      // Serial.print(uxTaskGetStackHighWaterMark( NULL ));
      // Serial.println();
      // Serial.flush();
      // vTaskDelay(7);
      xEventGroupSetBits( eg, evtTakeA_Reading );
      // delay(15);
    }
  }
  vTaskDelete( NULL );
} // void fUpdateDisplay( void * parameter )
////////////////////////////////////////////////////
void TaskAnalogRead_MyoWare( void *pvParameters )
{
  float vMyoWare0 = 0.0f;
  int temp;
  float ADscale = 3.3f / 4096;
  // https://dl.espressif.com/doc/esp-idf/latest/api-reference/peripherals/adc.html
  int j = 0;
  for (;;)
  {

    xEventGroupWaitBits (eg, evtTakeA_Reading, pdTRUE, pdTRUE, portMAX_DELAY) ;    
    if ( j <= dispValueCellCount )
    {
      for ( j = 1; j <= dispValueCellCount; j++)
      {
        vMyoWare0 = ( adc1_get_raw(ADC1_CHANNEL_0) * ADscale );
        vMyoWare0 *= 10.0f;
        temp = (int)vMyoWare0;
        xQueueSendToBack( xQ_Display, &temp, 0 );
        vTaskDelay(1);
      }
      j++;
      log_i( "j number is %d", j );
    } else {
      vTaskDelay( 1 );
      vMyoWare0 = ( adc1_get_raw(ADC1_CHANNEL_0) * ADscale );
      Serial.print( vMyoWare0 );
      vMyoWare0 *= 10.0f;
      temp = (int)vMyoWare0;
      // Serial.print( temp );
      Serial.println();
      xQueueSendToBack( xQ_Display, &temp, 1 );
    }
    xEventGroupSetBits( eg, evtUPDATE_DISPLAY );
  }
  vTaskDelete( NULL );
} //void TaskAnalogVoltRead_MyoWare( void *pvParameters )
////
/*
  Each I_XXX preprocessor define translates into a single 32-bit instruction. So you can count instructions to learn which memory address are used and where the free mem space starts.

  To generate branch instructions, special M_ preprocessor defines are used. M_LABEL define can be used to define a branch target.
  Implementation note: these M_ preprocessor defines will be translated into two ulp_insn_t values: one is a token value which contains label number, and the other is the actual instruction.

*/
void ULP_BLINK_RUN(uint32_t us)
{
  size_t load_addr = 0;
  RTC_SLOW_MEM[12] = 0;
  ulp_set_wakeup_period(0, us);
  const ulp_insn_t  ulp_blink[] =
  {
    I_MOVI(R3, 12),                         // #12 -> R3
    I_LD(R0, R3, 0),                        // R0 = RTC_SLOW_MEM[R3(#12)]
    M_BL(1, 1),                             // GOTO M_LABEL(1) IF R0 < 1
    I_WR_REG(RTC_GPIO_OUT_REG, 26, 27, 1),  // RTC_GPIO2 = 1
    I_SUBI(R0, R0, 1),                      // R0 = R0 - 1, R0 = 1, R0 = 0
    I_ST(R0, R3, 0),                        // RTC_SLOW_MEM[R3(#12)] = R0
    M_BX(2),                                // GOTO M_LABEL(2)
    M_LABEL(1),                             // M_LABEL(1)
    I_WR_REG(RTC_GPIO_OUT_REG, 26, 27, 0),// RTC_GPIO2 = 0
    I_ADDI(R0, R0, 1),                    // R0 = R0 + 1, R0 = 0, R0 = 1
    I_ST(R0, R3, 0),                      // RTC_SLOW_MEM[R3(#12)] = R0
    M_LABEL(2),                             // M_LABEL(2)
    I_HALT()                                // HALT COPROCESSOR
  };
  const gpio_num_t led_gpios[] =
  {
    GPIO_NUM_2,
    // GPIO_NUM_0,
    // GPIO_NUM_4
  };
  for (size_t i = 0; i < sizeof(led_gpios) / sizeof(led_gpios[0]); ++i) {
    rtc_gpio_init(led_gpios[i]);
    rtc_gpio_set_direction(led_gpios[i], RTC_GPIO_MODE_OUTPUT_ONLY);
    rtc_gpio_set_level(led_gpios[i], 0);
  }
  size_t size = sizeof(ulp_blink) / sizeof(ulp_insn_t);
  ulp_process_macros_and_load( load_addr, ulp_blink, &size);
  ulp_run( load_addr );
} // void ULP_BLINK_RUN(uint32_t us)
//////////////////////////////////////////////

It enables the read of such low currents as you muscle signals. Without the insulation you won't be able the read anything interesting with that device. Did you read the manual of your device?

Ahhh I see. I read the manual an thought that it was only for safety, didn't realise it could impact the results.

I'm looking at some now, what are the key specs an isolator needs to meet? The adafruit isolator is expensive, so I'm trying to determine a good alternative

Would either of these be a good option?

Unfortunately both devices have no useful documentation or schematics which would be necessary to really analyze if they are suitable. I would expect them to work as they use more or less the same components but it's your risk.

Hi noisyneighbour!

Sorry to hear your sensor is giving you some trouble setting it up.

I'm trying to get up to speed on this thread. You've gotten some really good advice so far!

If you're using a laptop, you can try unplugging the laptop's power cord and run it on battery instead of using a USB isolator. If you're using a desktop, I would recommend this USB isolator Adafruit USB Isolator - 100mA Isolated Low/Full Speed USB : ID 2107 : $34.95 : Adafruit Industries, Unique & fun DIY electronics and kits.

The only concerning thing I see from your pictures is that it looks like the reference cable is pulling the sensor off your skin slightly. I would recommend placing the reference cable so there's not enough tension to have this happen. Also, the sensor's switch is off in your pictures. Is that intentional?

Can you post a picture of the entire setup? I don't see any of the Arduino itself.

~ Brian

Hi,

The switch was off in the picture, I was just showing where I had placed it, I had it on during testing.

I'm currently troubleshooting the Adafruit USB I purchased, for some reason my computer now cannot see the serial port I'm using with the isolator connected. The port has disappeared from the arduino tools bar and in the device manager I get the follow message:

I got the isolator working, for some reason it causes an error when I connect the device in the low setting. When I connect it in the full speed setting it works fine and I can swap between full and low. Weird haha

Now that the isolator is working I'm getting great results!

1 Like