Has anyone ever tried to connecting myoware emg sensor to a nano in order to read muscle activity? I'm in need of help for a project, thanks!
Search for "EMG Muscle Sensor Module ".
The better "HEARTBEAT" sensor modules allow at least 3 to be connected.
Basically you need a signal amplifier such as the MAX 30100 or similar.
The voltages are too low without one to read reliably.
There are also muscle strain gauges
These may be more along what you need.
And just to get the creative juices flowing Arduino Muscle Sensor (EMG) Tutorial - YouTube
No but I have connected a EMG sensor (MAYOWARE) to a ESP32 to read muscle activity.
#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)
//////////////////////////////////////////////
Remember it's ESP32 code.
Here's one of our tutorials that shows you how to connect it to an Arduino Mini which is pretty similar. Bionic Wolverine Claws - MYOWARE by Advancer Technologies
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.