Building the first Electro-Mechanical Pedal Steel Guitar

Hy there again. I done some investigation and also connected two modules to re-program them. However, I learned that it does not save the addresses as I mentioned before. The only way is to do the following: you need t connect its VCC to digital output pins and use the below code. The code will then assign the addresses on start-up by switching the modules on as it program then. Open serial.print to see what is programmed. Code–GPIO 4 → TMAG #1 VCC
GPIO 5 → TMAG #2 VCC
GPIO 6 → TMAG #3 VCC
GPIO 7 → TMAG #4 VCC
GPIO 15 → TMAG #5 VCC
GPIO 16 → TMAG #6 VCC
GPIO 17 → TMAG #7 VCC
GPIO 18 → TMAG #8 VCC
GPIO 19 → TMAG #9 VCC
GPIO 20 → TMAG #10 VCC#include <Arduino.h>
#include <Wire.h>

// ============================================================
// ESP32-S3 I2C PINS
// ============================================================

#define SDA_PIN 8
#define SCL_PIN 9

// ============================================================
// TMAG5273 SENSOR POWER PINS
// One GPIO controls each sensor’s power
// ============================================================

const int sensorPowerPins[10] =
{
4,
5,
6,
7,
15,
16,
17,
18,
19,
20
};

// ============================================================
// TMAG5273 ADDRESSES
// ============================================================

// Factory address for TMAG5273A1/A2
#define TMAG_FACTORY_ADDRESS 0x35

// New addresses for sensors 1-10
const uint8_t sensorAddress[10] =
{
0x30,
0x31,
0x32,
0x33,
0x34,
0x36,
0x37,
0x38,
0x39,
0x3A
};

// ============================================================
// TMAG5273 REGISTERS
// ============================================================

#define TMAG_I2C_ADDRESS_REGISTER 0x0C

// ============================================================
// WRITE ONE BYTE TO TMAG5273
// ============================================================

bool writeRegister(uint8_t address, uint8_t reg, uint8_t value)
{
Wire.beginTransmission(address);

Wire.write(reg);
Wire.write(value);

uint8_t result = Wire.endTransmission();

return (result == 0);

}

// ============================================================
// CHECK IF DEVICE EXISTS
// ============================================================

bool deviceExists(uint8_t address)
{
Wire.beginTransmission(address);

return (Wire.endTransmission() == 0);

}

// ============================================================
// CHANGE TMAG5273 ADDRESS
// ============================================================

bool changeTMAGAddress(uint8_t newAddress)
{
/*
Register 0x0C:

   Bits 7:1 = new 7-bit I2C address
   Bit 0    = enable address update

   Therefore:

   newAddress << 1
   OR
   1
*/

uint8_t registerValue =
    (newAddress << 1) | 0x01;

return writeRegister(
    TMAG_FACTORY_ADDRESS,
    TMAG_I2C_ADDRESS_REGISTER,
    registerValue
);

}

// ============================================================
// POWER OFF ALL SENSORS
// ============================================================

void powerOffAllSensors()
{
Serial.println();
Serial.println(“Powering OFF all TMAG5273 sensors…”);

for (int i = 0; i < 10; i++)
{
    digitalWrite(sensorPowerPins[i], LOW);
}

delay(100);

}

// ============================================================
// ASSIGN ALL 10 ADDRESSES
// ============================================================

bool setupAllTMAGs()
{
Serial.println();
Serial.println(“======================================”);
Serial.println(" TMAG5273 ADDRESS SETUP");
Serial.println(“======================================”);

// --------------------------------------------------------
// Start with every sensor OFF
// --------------------------------------------------------

powerOffAllSensors();

// --------------------------------------------------------
// Do sensors one at a time
// --------------------------------------------------------

for (int i = 0; i < 10; i++)
{
    Serial.println();
    Serial.print("Sensor #");
    Serial.print(i + 1);
    Serial.println();

    // ----------------------------------------------------
    // Turn ON this sensor
    // ----------------------------------------------------

    digitalWrite(
        sensorPowerPins[i],
        HIGH
    );

    // Give TMAG5273 time to start
    delay(20);

    // ----------------------------------------------------
    // Check factory address
    // ----------------------------------------------------

    Serial.print("Checking factory address 0x");
    Serial.println(
        TMAG_FACTORY_ADDRESS,
        HEX
    );

    if (!deviceExists(TMAG_FACTORY_ADDRESS))
    {
        Serial.println(
            "ERROR: TMAG5273 not found!"
        );

        Serial.println(
            "Check power, SDA and SCL."
        );

        return false;
    }

    Serial.println(
        "TMAG5273 found."
    );

    // ----------------------------------------------------
    // Assign new address
    // ----------------------------------------------------

    Serial.print("Changing address to 0x");

    if (sensorAddress[i] < 0x10)
        Serial.print("0");

    Serial.println(
        sensorAddress[i],
        HEX
    );

    if (!changeTMAGAddress(sensorAddress[i]))
    {
        Serial.println(
            "ERROR: Address change failed!"
        );

        return false;
    }

    delay(5);

    // ----------------------------------------------------
    // Verify new address
    // ----------------------------------------------------

    if (deviceExists(sensorAddress[i]))
    {
        Serial.print(
            "SUCCESS: Sensor #"
        );

        Serial.print(i + 1);

        Serial.print(
            " = 0x"
        );

        Serial.println(
            sensorAddress[i],
            HEX
        );
    }
    else
    {
        Serial.println(
            "ERROR: New address not responding!"
        );

        return false;
    }

    // ----------------------------------------------------
    // IMPORTANT:
    //
    // Leave this sensor powered ON.
    //
    // Its new address prevents it from responding to
    // the factory address when the next sensor starts.
    // ----------------------------------------------------
}

Serial.println();
Serial.println(
    "======================================"
);

Serial.println(
    "ALL 10 TMAG5273 SENSORS CONFIGURED!"
);

Serial.println(
    "======================================"
);

return true;

}

// ============================================================
// SHOW ALL SENSOR ADDRESSES
// ============================================================

void scanTMAGs()
{
Serial.println();
Serial.println(
“Checking all 10 sensors…”
);

for (int i = 0; i < 10; i++)
{
    Serial.print("Sensor #");
    Serial.print(i + 1);

    Serial.print("  Address 0x");

    Serial.print(
        sensorAddress[i],
        HEX
    );

    Serial.print(" : ");

    if (deviceExists(sensorAddress[i]))
    {
        Serial.println("OK");
    }
    else
    {
        Serial.println("NOT FOUND");
    }
}

}

// ============================================================
// SETUP
// ============================================================

void setup()
{
Serial.begin(115200);

delay(1000);

Serial.println();
Serial.println();
Serial.println(
    "ESP32-S3 TMAG5273 STARTUP"
);

// --------------------------------------------------------
// Power GPIOs
// --------------------------------------------------------

for (int i = 0; i < 10; i++)
{
    pinMode(
        sensorPowerPins[i],
        OUTPUT
    );

    digitalWrite(
        sensorPowerPins[i],
        LOW
    );
}

// --------------------------------------------------------
// Start I2C
// --------------------------------------------------------

Wire.begin(
    SDA_PIN,
    SCL_PIN
);

Wire.setClock(400000);

delay(100);

// --------------------------------------------------------
// Assign addresses
// --------------------------------------------------------

if (setupAllTMAGs())
{
    scanTMAGs();
}
else
{
    Serial.println();
    Serial.println(
        "TMAG5273 STARTUP FAILED!"
    );
}

}

// ============================================================
// LOOP
// ============================================================

void loop()
{
// Nothing yet.
//
// The next step will be reading the
// 10 sensors and controlling the
// 10 servos.

delay(1000);

}

Well, after a long discussion on another forum, we discovered that the tmag chip does not remember its address on power down. So there’s really no way to use multiple on one buss without a multiplexer. That’s from the mfgr spec sheet, which I should have read all 50 pages of, I guess! chapter 7.2.2.2 says all user addresses are lost on power down. The way around is to use a seperate power gpio for each sensor, so you can turn off the ones you don’t want to address. And you have to do this sequentially on every power up. So that’s why the software doesn’t support addressing. It’s of pretty limited use.

So a multiplexer seems like its necessary. Here’s one that uses the connectors:

Also, what’s the longest i2c run you’ve done? They said on the arduino forum that you can have issues with more than 20 cm of wire with i2c. Did you use one eps for all the sensors in your knee levers? Maybe you didn’t use i2c? Looks like that may have been mistaken thinking on my part. I just liked that the connectors were already built on, as I have a tremor, and soldering is very hard for me.

I have separate assembies for the right and left knee levers, which you saw printed and one of which was working in the video above

It’s about 15 cm between boards. I could put one eps between each pair of levers. The eps has 2 i2c busses, right? So could I have two sensors with the same address on each of two eps boards. Then use a third one with a multiplexer for the five pedals. Alternatively, I could put a multiplexer and an eps between the left and right racks. But the run to the far board would be pushing the 20cm limit.

In better news, I built and did some basic testing of an interface for android that will do what I need for setting the servo ranges, using the bindCanvas library for eps. That’s fairly far along at this point…

But the easiest is to use 10 KY-024 Linear Magnetic Effect Sensor Hall Module Analog modules and connect to any analog in and its done.

So, in my setup I use one esp32 at the foot pedals and communicate via wifi to the top. At the top I have another esp32 that do the following. 1. Receive the pedal data. 2. Knee levers are connected to it. 3. It handles the fretboard LED logic. 4. It handles the phone app data to and from app. 5. It handles the esp Display. Then I have a Teensy that handle the following. 1. The frequency calculation. 2. It handles the Servo logic. 3. It handles the DC motor logic. 4. It handles the Manual and Auto string tuning. 5. It handles the Pedal/Lever auto calibration. It receives all needed data from top esp32.

How automatic is the tuning? What does the process look like?

I have no automatic tuning. In the app (which I’ll show a pic of when I get a chance) there is a slider to select a pedal. Once you’ve selected it, you can set min and max values for the raw values coming from the sensor, so there’s a clear value range to work with. Then you can select a string, and a there is a slider which acts like the selected pedal, and changes the pitch of the string. You move it right or left of center (there are inc-dec buttons also for fine adjustments) until the string is in tune, and then a button to store that value in memory. For now the range is 0-1000, with 500 for centered (servo at 90.) Then that particular change is tuned. I’m hoping it will stay consistent, and doesn’t have to be adjusted every day! Jacque has all kinds of auto-tune going on, but that’s not a priority for me, but I will look into implementing it if the changes drift. But hopefully they will be as stable as on a normal steel…

Select Auto Tune, select string and strum that string. DC motor will run string to target freq, stop/lock/save. In pedal and lever cal. you select say S2 RR, strum string2. String 2 will run to target freq, stop, lock and save Angle. It only take 3 to 5 sec per string. I am close to complete a mod which I will update all with pictures, illustration and play.

Neato, for public steel guitar I’m working on a per string optical pickup which hopefully will allow for retuning all open strings plus all pedals and levers with a single strum. Unsure if it will work but if it does it would be fun to hear all the strings wildly pitch up and down in a cacophony as it calculates the offsets.

Good luck on that. I initially done that. I have a pick-up with 12 coils. The issue I ran into which made me to put it on the backburner was that my power supply couldn’t handle the current and there were to mutch noise in the system caused by the power supply clipping. Also, one pick-up picked up 3 strings freq which made it inaccurate.

okay, that’s basically the same way I expect to allocate things.

Thanks Jacque for the code for starting up the TMAG units! …but the multiplexer will allow me to hook everything up with i2c cables, which seems worth the $15 or so in saved soldering. And I can stick with this TMAG unit, which I have done a lot of design around…

I sent my finger design to send-cut-send which is lasering them out of stainless. I’ll get those next week. Then build a quick plywood version of the body to test multiple strings on.

Time for an update…

I got code working for a phone app using the bind library, and preferences. I can set ranges for each pedal to affect each string. At the top I can set max and min raw values from the sensor. It’s easy to put a bit of a dead spot at either end of the pedal travel this way. Second row slider is to select the pedal, the third row sets the string it will affect. Fourth row slider will actually move the finger up or down. Center position of 500 = 90 degrees on the servo, the default. Once the slider has tuned the string correctly, you can press set tune, and it will be stored to prefs in the EPS, and used to scale the servo movement over the range of the pedal:

I am able to address my 4 hall effect sensors via the multiplexer. I have three servos hooked up via the servo driver, and can address them. I made a multi-string test bed. I am using a knife-edge pivot for the fingers, made from an actual knife:

I’ve also done a lot of design work on the body, figuring out how to lay everything out, and make sure I can route all the wiring. The bridge will be milled from 1" brass rod. The strings are wrapped around 4M screws set in the bridge at a 45 degree angle, similar to Moyo steel guitars. The body will be 3d printed in pieces, and laid up with carbon fiber. The strings sit on actual wood:

Some questions:

I can not figure out how to detach a servo via the servo driver, when it’s at rest position. The PWM driver for the servo board doesn’t seem to implement that. Can you still somehow call detach() on one of the servos? Seems not, since there’s no i2c to the servo itself, right? Is there a workaround for PWM?

I’m wondering if the servo driver board can handle 5 or 6 servos moving at once. I read that you can bypass the servo outputs on the board for + and -, wiring them direct to the psu, and just use the pwm pins. Did you need to do that, Jacque? The wires on my adapter are a bit thick to get in the screw terminals. I could solder them direct, but I wonder about the traces on the board handling the current. It’s 19 volts at 12 amps. Did you upgrade the capacitor on your board? Sounds like it might be a good idea. That said, these servos don’t seem to need to work very hard, if I get the return spring adjusted correctly.

I know Jacque you said you used a separate power supply for the ESP, but I’m wondering if there’s a way to pull power off the voltage converter, and step down both the current and voltage to use for the ESP. I’d sure rather not use another power supply…

Also, my psu cord has several of the magnetic barrels on it. I’m not sure I understand what they do on a power supply. I’d like to cut the cord pretty short, but I’m not sure if I should keep one or both of the barrels for some reason.

Another question is if it’s practical to run the LED strip (I have 1, with about 100 rgb leds) via the servo driver board. Or should I use a separate driver board for that? (I do have two…) Or can I just connect via i2c? If I do that, I’m wondering whether to piggyback all 3 (servos, led, hall sensors) on one I2C bus, or split one of them onto another bus. Does it matter? I’m not sure if there are bandwidth issues to consider.

Anyway, it’s proceeding slowly but surely. Thanks as always for your input!

-e

Hi Eric

Good to see your progress.

Answer. Servo’s does not give you feedback to know where it is at any time. However, the code gives the servo its target position via certain PWM sequence and the servo will go to that position and monitor that target position internally at all costs. What you should do in the code is to give it a target position of 90deg and wait say 3sec to make sure it get there and switch it to sleep mode. Just make sure if the string is in tune the spring hold it at 90deg position with servo disconnected otherwise when you disable the servo the string will move it.

To wake up a specific servo I use the function if pedal is at 0, if > 50 which will sent a command via PWM to operate.

I did not use a capacitor, the way I connected it is the servo motors use a separate power supply set to 5.5v. And yes, the power supply cannot handle all servos at a time so if the servos are at resting position they will never all need to run at the same time.

On my latest design I done what you mentioned above. I set the servos to 5.5V and use that to enter another regulator of 5v that feeds the esp32Master module.

The barrels are there to clamp the start and stop spikes, you can cut it off if you want, it is more for sensitive equipment like computers.

On my latest design I run the following from one ESP32S3: DC motors for string calibration. (Only enabled if Manual or Auto Tune selected). Servo’s (Only live if DC motors de-selected). 276 LEDs. Frequency input and calculation. Pedal Cal where I auto cal the following: pedal and knee lever travel, pedal and knee lever calibration.

My pedal data come from a esp32 at the bottom bar via WiFi.

I have a CroPanel display that connect to the ESP32s3 via TX/TX witch do all tuning and calibrations.

I also have an app that does the same as CroPanel and gets its uploading data via WiFi from ESP32s3, I can also connect to my pc via WiFi

All this with only one single esp32s3.

I had issues with slow data transfer but manage to sort it by prioritizing tasks (ESP run as normal but if I use a function that function will get priority)

If you want, I can send you a simple sample code to show how to manage servos and how to set them to off mode.

You however need to make sure the ESP do the priorities correct.

Hope all this help.