The Protocentral easyEMG is a single-channel surface EMG breakout that gives you the raw muscle waveform over I²C, not just an envelope. This guide takes you from the box to watching a muscle contract, first in the Arduino Serial Plotter and then in the OpenView desktop app, by way of the wiring, the electrodes and the one safety rule that matters most.
Research use only. easyEMG is for education, research and experimentation. It is not a medical device and must not be used for diagnosis or monitoring of any medical condition.
Before you put electrodes on anyone
easyEMG has no isolation. With electrodes on a person, the host must run from a battery, or connect through an easyISO isolated Qwiic adapter.
On a mains-powered computer, leakage current through the USB ground drives the reference amplifier to its rail, and you get no EMG at all, not a noisy trace. A laptop on battery is fine, as long as its charger is unplugged.
The same applies to test equipment. A mains-earthed bench scope connects the person wearing the electrodes to earth, so use a battery-powered scope.
Introduction
A contracting muscle produces a small, fast, zero-mean signal at the skin. Many muscle sensors rectify and smooth that signal in hardware and hand you only the envelope, so the waveform underneath is gone. easyEMG keeps it:
- A TI INA333 instrumentation amplifier takes the difference between two electrodes on the muscle, with a gain of ×11.
- An RC high-pass at about 20 Hz removes electrode offset and motion baseline.
- A TI OPA2333 op-amp adds a gain of ×4.7, for ×51.7 in total.
- A Sallen-Key low-pass limits the band to about 310 Hz.
- A TI TLA2024 12-bit ADC digitises the result and sends it over I²C.
- A further OPA2333 stage and a third electrode form a driven reference, which holds the body near the amplifier’s common mode.
The Arduino library computes the RMS envelope in software, so you get the “how hard is this muscle working” number and can still go back to the signal behind it.
Key Features
- Raw band-limited waveform, read in microvolts at the electrodes.
- RMS envelope, activation detection and %MVC in the library.
- Labelled test points: INA OUT, OUT, DRL and VMID, plus a GND point for your probe clip.
- Two Qwiic / STEMMA QT ports for daisy-chaining, plus a 0.1″ header for 3.3 V or 5 V hosts.
- Shares a bus with easyECG: easyEMG ships at 0x48 and easyECG at 0x49.
- Open hardware (CERN-OHL-P v2) and an MIT-licensed library.
What’s in the Box
| Qty | Item |
|---|---|
| 1 | Protocentral easyEMG breakout board |
| 1 | 1×4 0.1″ header (not soldered) |
| 1 | 3-lead snap electrode cable, 3.5 mm TRS |
| 10 | Disposable Ag/AgCl snap electrodes |
A Qwiic cable and a host board are not included.
Specifications
| Parameter | Value |
|---|---|
| Channels | 1 differential sEMG channel, 3 electrodes (E1, E2, REF) |
| Gain | ×51.7 total (×11 in-amp, ×4.7 second stage) |
| Analog bandwidth | About 20 – 310 Hz (−3 dB) |
| Input range | ±4.95 mV; electrode offset tolerance ±145 mV |
| ADC | TI TLA2024, 12-bit, ±0.256 V range |
| Resolution | 2.42 µV per count at the electrodes |
| Sample rate | Set by how fast your host polls; see below |
| Interface | I²C / Qwiic, address 0x48 (0x49 via the ADDR jumper) |
| Supply | 3.3 V on Qwiic; 3.3 – 5 V on the header |
| Reference current limit | 390 kΩ (≤ 4.2 µA into the body) |
| Board | 40.0 × 28.8 mm, 4 × M2 mounting holes |
Hardware Overview
| Test point | What you’ll see there |
|---|---|
| INA OUT | The in-amp output, ×11, including the electrode offset |
| OUT | The final signal going into the ADC, ×51.7, centred on VMID |
| VMID | The buffered 1.65 V mid-rail that the signal is centred on |
| DRL | The driven-reference output |
| GND | Ground, for your probe clip |
I²C address
The board ships at 0x48. To move it to 0x49, cut the bridge on the ADDR jumper and bridge the 0x49 side, then construct the library object with easyEMG emg(EASYEMG_I2C_ADDR_ALT);.
Wiring
Option 1 — Qwiic (solderless)
Connect a Qwiic / STEMMA QT cable from your board’s Qwiic port to either port on easyEMG. That’s all: the cable carries power, ground, SDA and SCL. The Qwiic port runs at 3.3 V.
On some boards the Qwiic port is a second I²C bus. On an Arduino UNO R4 WiFi, for example, it’s Wire1, so call emg.begin(Wire1); and Wire1.setClock(400000);.
Option 2 — I²C over the 0.1″ header
No Qwiic port? easyEMG needs only two I²C lines plus power and ground. Solder the included header and wire it up:
| easyEMG pin | Arduino Uno / Nano | ESP32 (default I²C) | Raspberry Pi Pico | Raspberry Pi |
|---|---|---|---|---|
| VCC | 5V | 3.3V | 3V3 OUT (pin 36) | 3V3 (pin 1) |
| GND | GND | GND | GND (pin 38) | GND (pin 6) |
| SDA | A4 | GPIO21 | GP4 (pin 6) | GPIO2 (pin 3) |
| SCL | A5 | GPIO22 | GP5 (pin 7) | GPIO3 (pin 5) |
SCL is the square pad. The header takes 3.3 – 5 V and has its own regulator and level shifting, so a 5 V Arduino can drive it directly. On the Pico and the Raspberry Pi, use the MicroPython driver rather than the Arduino library.
Through easyISO
For a mains-powered host, connect the host to the IN side of an easyISO and easyEMG to the OUT side. No code changes are needed. See the easyISO guide.
Placing the electrodes
Plug the cable into the 3.5 mm jack and snap a fresh electrode onto each lead. Each muscle uses the same three leads:
| Lead | Colour | Jack contact | Where it goes |
|---|---|---|---|
| E1 | Green | Sleeve | On the belly of the muscle |
| E2 | Red | Ring | On the belly of the muscle, next to E1 and in line with the muscle |
| REF | Yellow | Tip | On a bone close to the muscle, with no muscle under it |
Start with the biceps. It’s the largest muscle here and the easiest to load, so it should give the clearest bursts. Move on to the forearm once you’ve seen a good signal.
Biceps
- Sit with your elbow bent to about 90° and your forearm resting on a table.
- Press your palm up against the underside of the table and feel for the fullest part of the biceps. It’s about a third of the way up from the elbow crease towards the shoulder.
- Put E1 and E2 on that spot, one above the other, in line with the arm. Keep the pads close together without overlapping.
- Put REF on the bony point on the inner side of your elbow.
To test it: rest for five seconds, then curl a water bottle or press up against the table for five seconds. Repeat a few times. The raw trace should burst when you lift, and settle when you stop.
Forearm
- Rest your forearm on a table, palm up.
- Clench your fist and feel for the muscle that bulges on the inner forearm, about a third of the way down from the elbow crease towards the wrist.
- Put E1 and E2 on that bulge, in a line running from the inner elbow towards the middle of the wrist. Keep the pads close together without overlapping.
- Put REF on the bony knob on the little-finger side of your wrist.
To test it: rest for five seconds, then clench your fist for five seconds. Repeat a few times. Several muscles sit close together here, so moving your fingers or wrist changes the signal too. That’s normal.
Getting a clean signal
- Wipe the skin with alcohol and let it dry before you stick anything on.
- Give the gel a minute or two to settle.
- Press REF firmly onto the bone. Most noisy traces come down to electrode contact.
- Tape the cable to your arm, so it can’t tug on the snaps when you move.
- Keep your arm still and supported between contractions, and relax fully when you zero the baseline.
- Re-zero with
calibrateBaseline()each time you move the electrodes.
Installing the library
- In the Arduino IDE, open Tools → Manage Libraries.
- Search for ProtoCentral easyEMG and click Install.
Or download the library from GitHub and use Sketch → Include Library → Add .ZIP Library. It has no other dependencies.
Your first sketch
#include <Wire.h>
#include "ProtoCentral_easyEMG.h"
easyEMG emg;
void setup() {
Serial.begin(115200);
if (!emg.begin()) {
Serial.println("easyEMG not found - check wiring and I2C address (0x48).");
while (1) delay(1000);
}
Wire.setClock(400000);
// Relax the muscle for this: it zeroes the resting offset.
emg.calibrateBaseline();
emg.setEnvelopeWindow(64);
}
void loop() {
float envelope = emg.readEnvelope(); // uV RMS: "how hard is this muscle working"
Serial.print("Raw_uV:");
Serial.print(emg.lastMicrovolts(), 1);
Serial.print(",Envelope_uV:");
Serial.println(envelope, 1);
}
Disconnect the charger if you’re on a laptop, upload the sketch with the muscle relaxed, then open Tools → Serial Plotter at 115200 baud.
Reading the trace
At rest you should see a small, fuzzy trace close to zero. Clench your fist or flex the muscle and the raw trace bursts, while the envelope rises and falls with your effort.
As a rough guide, in µV RMS at the electrodes:
| Condition | Envelope |
|---|---|
| Good electrodes, relaxed | 5 – 15 |
| Light contraction | About 100 |
| Strong contraction | About 1000 |
Absolute microvolts use the design gain of ×51.7. Activation thresholds set from your own noise floor and %MVC are ratios, so they don’t depend on it.
Sample rate
easyEMG has no data-ready pin, so the rate is set by how fast your host polls I²C. A rate of fs only captures signal up to fs / 2, and the analog band runs to about 310 Hz. For the whole band, use a 32-bit board and call Wire.setClock(400000). An 8-bit Arduino or a Python host runs slower, which narrows the usable band, though the envelope still tracks effort. 04.EnvelopeRMS and 07.Diagnostics print your own rate.
The envelope window is counted in samples, so it depends on that rate. Aim for 50 – 200 ms, and use envelopeWindowMillis() to convert.
Mains hum
There’s deliberately no 50/60 Hz notch in hardware, because mains sits inside the EMG band and removing it costs real signal. Fix the cause first: fresh electrodes, REF on bone, and a host that isn’t on mains. Even on a battery-powered laptop, room lighting can put hum on the body at two and three times the mains frequency (100 and 150 Hz on 50 Hz mains) rather than at the mains frequency itself.
If you still need it, the library notches it in software. setNotch() removes the fundamental only. setMainsNotch() notches every harmonic up to 0.45 times the sample rate (450 Hz at 1000 SPS), 6 Hz wide at 50/60 Hz and 2 Hz wide on the rest, and 06.OpenView uses it by default. Both need your exact sample rate and a paced loop, as 06.OpenView and 07.Diagnostics show.
Seeing it in OpenView
OpenView is our desktop app for live plotting and recording. It shows the raw sEMG and its RMS envelope as two strips, in µV at the electrodes, with your effort as a percentage of your maximum contraction beside them.
- Install OpenView 3.4 or later. Earlier versions have no easyEMG profile and can’t read this stream. Keep your laptop on battery with the charger unplugged.
- Open File → Examples → ProtoCentral easyEMG → 06.OpenView. Its mains notch is set for 50 Hz, so change
MAINS_HZto 60 if that’s your mains frequency. Upload it. - Close any Serial Monitor or Serial Plotter window, because they hold the port.
- In OpenView, open Connect, choose USB and select easyEMG Breakout.
- Record your maximum contraction: see Effort and your MVC below. The sketch also runs this step by itself after every reset, and opening the serial port resets some boards, so relax as you connect.
Effort and your MVC
Microvolts vary from person to person, muscle to muscle and even with where you stick the pads, so a raw envelope can’t tell you how hard you’re working. Effort can. It’s your envelope as a percentage of your maximum voluntary contraction (MVC), the strongest contraction you can make:
Effort (%MVC) = envelope ÷ MVC × 100
The envelope here is the one OpenView plots: the RMS of the last 128 ms of signal, smoothed over about 120 ms. The MVC is the highest value of that same envelope while you contract as hard as you can. Relaxed reads a few percent; a strong contraction reads close to 100.
Recording it
The MVC tile in OpenView walks you through it, and the board’s LED shows the same steps:
| Step | MVC tile | LED | What to do |
|---|---|---|---|
| 1 | Relax… | Off | Keep the muscle relaxed for 2 seconds. The filters settle, and the last second is measured as your resting level |
| 2 | Contract hard now | On | Contract as hard as you can and hold it until the LED goes out, 5 seconds later |
| 3 | Recorded | Off | Done. Effort now reads against this MVC |
Press Record MVC in OpenView to run it again at any time. The button sends the character m to the board, so typing m in the Serial Monitor does the same when OpenView isn’t connected.
To get a good MVC:
- Hold against something that doesn’t move. For the forearm, grip a rolled towel or push your palm up against the underside of a table. Contracting against resistance without moving the joint (an isometric contraction) gives the strongest, steadiest signal, and movement adds artefact.
- Build up over the first second, then hold. The highest point counts, so there’s no need to jerk.
- Record it again whenever you move or replace the electrodes. The MVC belongs to that placement on that muscle, so compare effort within a session, not between days.
When an MVC is refused
A reference taken from a relaxed or barely tensed muscle would make every later reading several hundred percent. So the sketch refuses an MVC that’s below 100 µV or below 5 times your resting level. The tile shows Too weak: try again, the LED blinks for 2 seconds, and the previous MVC stays in use. Until one is recorded, the tile reads Not recorded and effort reads 0%.
With MVC_AUTO_RAISE on, the default, a later contraction that’s stronger than your MVC becomes the new MVC, so effort never goes over 100% and a weak reference corrects itself on your first hard contraction. A movement artefact that big would raise it too. For measurements, set MVC_AUTO_RAISE to false and record the MVC carefully. If effort ever reads over 100%, OpenView shows it in amber with Above your MVC: record it again.
Settings
Change these at the top of 06.OpenView:
| Setting | Default | What it does |
|---|---|---|
RECORD_MVC |
true |
Run the MVC step after every reset |
MVC_SETTLE_MS |
2000 | Relax time before the LED lights; the last second sets the resting level |
MVC_RECORD_MS |
5000 | How long the LED stays lit for the contraction |
MVC_MIN_UV |
100 | Smallest MVC accepted, in µV |
MVC_MIN_REST_RATIO |
5 | Smallest MVC accepted, as a multiple of the resting level |
MVC_AUTO_RAISE |
true |
Let a stronger contraction become the new MVC |
MVC_COMMAND |
'm' |
Character that runs the step again |
In your own sketch, record the peak envelope yourself, pass it to setMVC() in µV, and read effort with readPercentMVC(), as 04.EnvelopeRMS does. That example takes a simple 5-second peak, without the resting check.
Other examples
Find these under File → Examples → ProtoCentral easyEMG:
| Example | What it does |
|---|---|
01.BasicReading |
Raw counts and microvolts |
02.SerialPlotter |
Muscle activity in the Serial Plotter |
03.Calibration |
Baseline zeroing and your resting noise floor |
04.EnvelopeRMS |
RMS envelope, MVC recording, %MVC and a live sample-rate readout |
05.MuscleActivation_LED |
Flex to light an LED, with an auto-set threshold |
06.OpenView |
Streams to the OpenView desktop app (3.4 or later); select the easyEMG Breakout profile |
07.Diagnostics |
I²C scan, config read-back, ADC check, sample rate and a notch comparison |
Troubleshooting
Start with 07.Diagnostics. Flat, noisy and dead traces can look alike, and this sketch tells them apart.
| Symptom | Likely cause | What to try |
|---|---|---|
| “easyEMG not found” | Wiring, or a different address | Check the cable and run 07.Diagnostics. The board ships at 0x48 |
| No EMG, trace pinned | Mains-powered host | Unplug the charger, or use an easyISO |
| Huge signal that rails the ADC | Electrode not attached | Floating inputs act as antennas. Check all three snaps and pads |
| Noisy trace, small envelope change | Electrode contact | Use fresh pads, alcohol-wiped dry skin, and REF firmly on bone |
| Mains hum that a notch doesn’t remove | Unpaced sampling | The notch needs your exact rate. See 07.Diagnostics |
| Effort far above 100%, or MVC tile says Too weak | MVC recorded from a relaxed or barely tensed muscle | Press Record MVC, relax for 2 s, then contract as hard as you can while the LED is lit |
| Effort stays at 0% | No MVC recorded yet | Press Record MVC, or reset the board and follow the LED |
| Large, steady reading that never varies | Stale baseline | Relax, then call calibrateBaseline() again after every electrode change |
Where next
- Product page: Protocentral easyEMG
- Arduino library: protocentral_easyemg_arduino, including a full troubleshooting guide
- MicroPython and Raspberry Pi driver: protocentral-micropython-easyemg
- Schematic and KiCad files: schematic (PDF) · hardware repo
- Datasheets: INA333 · OPA2333 · TLA2024
- Isolation: easyISO isolated Qwiic adapter
- Heart signals on the same bus: Protocentral easyECG





