Config info for custom wiring

Hi,
I’ve built an analog synth about 40 years ago and now I’m trying to build a custom Zynthian.
However I’m struggling very much with the MCP23017 expansion. I have 4 encoders (with switches ) connected but the info on config is rather hidden. AI (Le Chat Mistral) helped a bit, but turned in circles finally.
My encoders are connected like this:
1 - GPA2 B1 - GPA1 SW1 - GPA0
A2 - GPA5 B2 - GPA4 SW2 - GPA3
A3 - GPB2 B3 - GPB3 SW3 - GPB0
A4 - GPB5 B4 - GPB4 SW4 - GPB3
These correspond to the connections I found in a schematic for Zynthian mini based on Zynthian all in one.

My zynthian_envars.sh contains this

#Zynthian Wiring Config

export ZYNTHIAN_WIRING_LAYOUT=“CUSTOM”
export ZYNTHIAN_WIRING_ENCODER_A=“101, 105, 110, 113”
export ZYNTHIAN_WIRING_ENCODER_B=“102, 104, 109, 112”
export ZYNTHIAN_WIRING_SWITCHES=“100,-1,-1, 103,-1,-1,-1,-1,108,-1,-1,111,-1,-1, -1, -1, -1”
export ZYNTHIAN_WIRING_MCP23017_I2C_ADDRESS=“0x20”
export ZYNTHIAN_WIRING_MCP23017_INTA_PIN=“27”
export ZYNTHIAN_WIRING_MCP23017_INTB_PIN=“25”


With this I get errors:
Dec 25 15:26:30 zynthian startx[3500]: ZynCoder->update_polled_zynswitches(0): Wrong pin number 108 in zynswitch 8!
Dec 25 15:26:30 zynthian startx[3500]: ZynCoder->update_polled_zynswitches(0): Wrong pin number 111 in zynswitch 11!

I would be so glad if somebody could help me find the cause of this error, or give me a location where I could find more info on the config.

Thanks a lot in advance!

1 Like

As I am familiar with Python, it would eventually be useful for me to have a look at the software structure. And it would interest me.

Welcome aboard the good life boat zynthian!

The first thing to check when dealing with encoders is which of he two types you have. Do they have three connections or four? The four connection devices are intended to be used with a power supply connection and do not generally work on a Zynth.

A quick picture posted here will resolve many issues down the line.

The allocation of the pins on the zynth can also be an involved process as there are two numbering sequences used which is an upshot of the Raspberry Pi wiring itself. One refers to the acual 1/0 pins on the gpi connected and the other references the internal connections within the raspberry Pi chip itself which are different.

Don’t worry we will get you throu’ this process, as we have done many times in the past. ..
I’m just passing throu at the moment so can’t hold forth at length but we can sort this reasonably quickly by simply being methodical.

Just as advice it’s always a good idea to start with the encoder switches especially the select switch as this does more work than the others and once that works it means you have a handle on both the hardware and the software. IT is a rare self build that will get these all correct the first time, especially with the requirements for the encodes which require two correctly wired pins to work and that’s if they aren’t the four pin encoders.

Documentation link added subsequently.

In truth I’d forgotten it was out there.

and here’s another walkthrou’ made by @hannesmenzel

Thank you so much for your fast and gentle reaction!
OK, I have already checked some things.

  • The MCP23017 is alive and has address 0x20.
  • Encoders should work, they are on 5 pin PCBs with Vcc, GND, CLK = A, DT = B, SW
    powered with 3.3V
    Signals on CLK and DT are Ok, the osci says
  • MCP23017 is also powered with 3.3V (datasheet says that is OK)
  • I have not investigated in the datasheet yet how the interrupts work. When I measure, A=H, B=L.
    Shouldn’t they both be H?
    For the rest the synth works (audio with PCM5102 = OK, MIDI = OK, screen = OK, web interface = OK)
    I could mention that I started with a headless configuration as described on the web page.

Anyway, the problem seems to be in the configuration, not in the hardware.
Am I right that for the switches I have to use 16 values, all -1 except for those I have connected, and with the numbering A0, A1, … , B0, B1, …B7 ?
Also it was not clear at the beginning for me that I have to use numbers e.g. A0 = 100 and not 0.
In the discussion with Le Chat Mistral we turned in circles between numbering 0,1,2… and 100, 101, ….

I am looking forward to hear more … :slight_smile:

1 Like

Generally those are the one’s zynthian’s don’t use. So a tediously involved wyleuesque explanation is in order, for various reasons, nott withstanding the desire not to write the second installment of the fable… You have been warned.

The zynth in it’s early days was a lesson in pragmatism, and ease of construction.
The encoders were spread across zynthian i/o pins and a curious 27008 based expander board that was all held together with a bit of ribbon cable with breadboard sockets plugged into the connector at the end of the ribbon cable.
The problems weren’t so much deep levels of software or hardware but simple connector issues. And it only had 4 encoders…

So simplicity was the name of the game.
In light of this the encoder wiring was as simple as it could possibly be.

A 0V line that everything connected to via the various switches and three sense wires that the Pi examined as frequently as it could to check whether or not the big blathering lump of humanity, that flattered itself it was in control, had actually decided to do anything. . . .

The Pi like most modern i/o has settled on a fairly standardized approach for presenting a chunk of electronics to be used and abused by said humanity, frequently armed with hot soldering irons and disturbingly incorrect
voltages and connections.
|| No. || Option || Explanation ||
|1 | Make the pin an output…| Easiest. Really the software sends logic values to the port and the port obeys. 1, True, A value for +supply voltage and 0, False, No Value for 0V and with such devices one can demonstrate some inner perception of the Pi’s software to the outside world. . . .|
| 2 | Make the Pin an Input | The obvious alternative to the above. Where by external information is communicated to the Pi’s software by external force, frequently enacted by the attached humanic blob. |

The input option is obviously what we are dealing with here, but there are subtleties.
The electronic world is a big rough place. Things like 240 Volts of mains voltage are out there and in the perpetual drive for low cost these are on occasion connected to devices similar to a Pi with circuits that would scare the living daylights out f must self regarding electrical engineers. But it happens and the reason this sort of thing can be done is down to an early realisation. If the input regarded pretty much any signal that wasn’t a specific value as one input signal and a specific value as the other , then any multitude of sins could be addressed, just this side of go bang. And to aid this all that was needed was a cleverly placed resistor to accommodate this intriguing concept.

A resistor was connected between the Supply rail ( +3.3V) and the input pin. This is all accomplished by the IC when the Pin is configured in the software and happens as if by magic.
And the result is a great aid to simplicity and ruggedness. Because the only signal needed to indicate the other logic state is to connect the input pin to Ground. Ov. No need to provide two way switches to provide a logic one or zero, no need to wire up lots of supply voltages to the controls to indicate the two states. Just a simple disconnect most of the time and then a connection to 0 to indicate the other state.
And this is what most encoder in the zynthian world do. Because it’s dead simple, there is little to go wrong and it’s easy to fault find. The one consideration is that the limits of what a particular pin can take can be pretty limited. And it the case of most hobbyist electronics it’s 3.3 Volt and connecting it to 5 volts will probably fry it permanently. Another reason to stay away from specific supplied values.

Your encoders are designed for a more rugged power supplied world. But the good news is supply them with 3.3v and the 0V and they should just work. at the end of the day the input pin is simply looking for one of two values and that’s all it actually cares about.

The Encoder itself is a simple beast. There are two circular tracks of a certain number of conductive patches etched into a circuit board. The number of patches in the circles set the number of clicks you get per turn.

But the clever bit is they are offset in phase by 90 degrees . Without going throu the logic of it all, one can detect with a little software extraction , the pulses as the encode is turned. And cleverer still, the direction…
This fits in particularly well with out only off’s matter mentality of the Pull up resistor described above. One only needs connect the slider of both encoder strips to OV and then connect each ring on the pcb to the INput pin and hey presto! Encoder to Pi with 3 wires, no external connections or electronic require. Well appart from capacitors to provide a bit of anti bounce because the Pi is able to read the encoder several times as the slider bounces over the PCB track and electric musical instruments live in noisy environments.

All the encoders with +V & 0V connections provide is these pull up resistors on the circuit board attached to the encoder. Not an issue if you connect the +3.3V it should all just work. But leave the +V disconnected and it don’t work, because the resistors on the circuit board and the pull up resistor in the Pi produce a voltage that simply doesn’t cross the threshold and nothing happens. We have seen this occur several times in Zynth build so it’s worth mentioning.

You also don’t get any joy if one of the encoder A or B pin isn’t connected so the less connections between the encode and the Pi pin the better!

The fact you can see the MCP23017 as 0x20 hex is great confirmation and provides the reason why the modern zynth farms the handling of encoders off to a seperate chip over a bus. A much more maintainable solution!

The use of the interrupt, saves a lot of repeated looking, (poling as it’s known). The 23017 generates an interrupt on a change and the Pi can go and have a quick look to see what has changed.

All very wonderful till the Interrupt doesn’t work, generally for wiring problems. IT needs to know which pin to expect the stimuli on and this it has to be told during the wiring set up, and then there’s all the confusion of Pi pin numbering. . .

But you are getting there, a multi meter is a good way of checking things out or failing that a low voltage battery an LED and resistor, just to make sure what you believe is connected is actually connected, and or connected to something it shouldn’t be. . .

And remember this little LED resistor and battery is a good way of checking an encoder. Connect it between pins A & B and turn the encoder and you should wee the LED flicker as the encoder is turned.

One wonders how I get quite so many words out of such simple concepts.

Hi wyleu,
I understand now what you are meaning by “wyleuesque explanation”.:slightly_smiling_face: :nerd_face:
What you tell me is not really new to me. Unfortunately it doesn’t help me.
I know how to connect an encoder or to control pin states using an oscilloscope.

To make it short:
Yes, the encoder DO WORK, as I have controlled with the osci. They have nice 0V / 3.3V pulses.
This cannot be the problem.

As I get these errors:
Dec 25 15:26:30 zynthian startx[3500]: ZynCoder->update_polled_zynswitches(0): Wrong pin number 108 in zynswitch 8!
Dec 25 15:26:30 zynthian startx[3500]: ZynCoder->update_polled_zynswitches(0): Wrong pin number 111 in zynswitch 11!

the problem must lie here I think:
export ZYNTHIAN_WIRING_SWITCHES=“100,-1,-1, 103,-1,-1,-1,-1,108,-1,-1,111,-1,-1, -1, -1, -1”
and this independantly of encoders working or not.

It is also interesting that INTB remains low, maybe because both problems are on port B ???

Please tell me if my configuration is OK when I have the configuration of encoders:
1 - GPA2 B1 - GPA1 SW1 - GPA0
A2 - GPA5 B2 - GPA4 SW2 - GPA3
A3 - GPB2 B3 - GPB3 SW3 - GPB0
A4 - GPB5 B4 - GPB4 SW4 - GPB3

ZYNTHIAN_WIRING_SWITCHES defines the switches, not the encoders. It is a comma separted list of GPI indicies for each switch in switch order, i.e. the first entry (100 in your example) is for switch 0. There should be the same quantity of entries in this list as there are physical or virtual switches.

ZYNTHIAN_WIRING_ENCODER_A and ZYNTHIAN_WIRING_ENCODER_B define the GPI used for encoders. These are comma separated lists of the GPI pins used for each encoder. ZYNTHIAN_WIRING_ENCODER_A is one pin on the encoder and ZYNTHIAN_WIRING_ENCODER_B is the other pin on the encoder so, for 4 encoders both of these would have 4 elements in their lists, e.g. ZYNTHIAN_WIRING_ENCODER_A=“102,105,110,113”.

?
This
export ZYNTHIAN_WIRING_LAYOUT=“CUSTOM” export ZYNTHIAN_WIRING_ENCODER_A=“101, 105, 110, 113” export ZYNTHIAN_WIRING_ENCODER_B=“102, 104, 109, 112” export ZYNTHIAN_WIRING_SWITCHES=“100,-1,-1, 103,-1,-1,-1,-1,108,-1,-1,111,-1,-1, -1, -1, -1” export ZYNTHIAN_WIRING_MCP23017_I2C_ADDRESS=“0x20” export ZYNTHIAN_WIRING_MCP23017_INTA_PIN=“27” export ZYNTHIAN_WIRING_MCP23017_INTB_PIN=“25”

or something different?

First, your config seems to have some typos… I guess the first “1” should be “A1” and that “B3” is connected to “GPB1”. Assuming my assumptions are valid… I expect config to be:

ZYNTHIAN_WIRING_LAYOUT=“CUSTOM”
ZYNTHIAN_WIRING_ENCODER_A=“102,105,110,113”
ZYNTHIAN_WIRING_ENCODER_B=“101,104,109,112”
ZYNTHIAN_WIRING_SWITCHES=“100,103,108,111”
ZYNTHIAN_WIRING_MCP23017_I2C_ADDRESS=“0x20”
ZYNTHIAN_WIRING_MCP23017_INTA_PIN=“27”
ZYNTHIAN_WIRING_MCP23017_INTB_PIN=“25”

You may want/need to add more dummy switch entries (“-1”) if you are using the on-screen keypad display.

It’s mostly making sure all the bases have been covered it also refines the description from my end.

We are ultimately refining documentation for the rare and strange hinterland between a prescribed and buy-able kit, and the incredibly wide range of the open source lot.

It might well help someone else who finds your thread. We have had a lot of trouble with mis-wired encoders and we do get to perform these long standing hits fairly frequently.

And once this works, and it will, I will come along again and ask you for a snippet of Audio as a communal reward.

It’s all sort of explained here. . .

OMG, you’re right. My head must have got dim with all these try and error sessions!
I have checked and in fact there ARE errors in my connection table!
**
A1 - GPA2 B1 - GPA1 SW1 - GPA0
A2 - GPA5 B2 - GPA4 SW2 - GPA3
A3 - GPB2 B3 - GPB1 SW3 - GPB0
A4 - GPB5 B4 - GPB4 SW4 - GPB7

Sorry for that!

I’ll check again soon and correct the config file!

It might well help someone else who finds your thread. We have had a lot of trouble with mis-wired encoders and we do get to perform these long standing hits fairly frequently.

That’s a point for you!
I understand.

Hopefully I will find the time to document my Zynthian when everything is working.

Are you sure you connected SW4 to GPB7? Logically it would be GPB3.

A methodical approach on encoder mapping is often the a good starting approach to fault finding. Get the select encoder switch working first. Rather than attempting to wire and map the entire system as this can lead to all kinds of irritating problems. Accidentally swap a couple of encoder pins with each other or one dry point and things occasionally work, or don’t work at all.

Hardware can and does override the best efforts of software more frequently than we would care to admit.

In fact I wanted to start as simple as possible that is to say with only 1 encoder. But then I was confused with all the info that didn’t match (as I said already AI didn’t really help, and i didn’t find one place explaining the address numbers, for example the numbering 100, 101…, instead of 0,1, … and also numbering 108, 109… for the B port.)
Then I thought maybe the software expects at least 4 encoders, that’s why I did it with 4.

Yes, unfortunately there are several mappings at work here and it’s easy to get a couple of encoders mixed up and chaos ensues.

And once you pin down the fault do you adjust he hardware or software? The later is obviously easier. In fact the real problem at that point is that one doesn’t ( and I haven’t on several occasions) not made careful notes on the arrangement that DOES actually work.
This becomes a problem when you burn a new SSD as you will be expected to provide the mapping specific to your own device and this can mean working it all out from first principles again…
In truth I use the forum to keep track of my various home brews mostly by name zynthian-pedal.local, zynthian-rack7.local, etc The 7 is the seventh rebuild of that particular machine, and I have backups of the previous six, just in case.

Yes.
Here is my wiring, in colour, and with the addresses I think should be the right ones:

There are or were i2c methods similar to i2cdetect that will tell you what individual pins are doing ..

sudo apt install wiringpi
gpio readall

was one way but that now ( like a lot of GPIO stuff) doesn’t work on Pi 5’s

there is GitHub - Milliways2/GPIOreadall: GPIOreadall is a replacement for the deprecated wiringpi gpio readall utility on Raspberry Pi.

But I’ve not tried it.

D’Oh. Sorry I keep thinking this is all on GPIO pins.
It might be worth writing a little bit of python to check what is actually being recieved.

If you try this, just turn off the zynthian components

systemctl stop zynthian

systemctl stop zynthian-webconf

To stop them restarting every ten seconds or so, which is rather confusing till you realise that’s whats going on.

import smbus
import time

Initialize I2C bus (on Raspberry Pi 5, typically bus 1)

bus = smbus.SMBus(1)
address = 0x20  # MCP23017 I2C address

Set all pins on Port A and Port B as inputs (0xFF = all inputs)

bus.write_byte_data(address, 0x00, 0xFF)  # IODIRA
bus.write_byte_data(address, 0x01, 0xFF)  # IODIRB

Registers for reading GPIO values

GPIOA = 0x12
GPIOB = 0x13

Read initial states

prev_a = bus.read_byte_data(address, GPIOA)
prev_b = bus.read_byte_data(address, GPIOB)

print(“Monitoring MCP23017 inputs for changes…”)

while True:

Read current states

curr_a = bus.read_byte_data(address, GPIOA)
curr_b = bus.read_byte_data(address, GPIOB)

# Check for changes on Port A
if curr_a != prev_a:
    for pin in range(8):
        mask = 1 << pin
        if (curr_a & mask) != (prev_a & mask):
            new_value = 1 if (curr_a & mask) else 0
            print(f"GPA{pin} changed to {new_value}")

# Check for changes on Port B
if curr_b != prev_b:
    for pin in range(8):
        mask = 1 << pin
        if (curr_b & mask) != (prev_b & mask):
            new_value = 1 if (curr_b & mask) else 0
            print(f"GPB{pin} changed to {new_value}")

# Update previous states
prev_a = curr_a
prev_b = curr_b

# Sleep for a short interval to avoid excessive I2C traffic (adjust as needed)
time.sleep(0.1)


This shouldn’t need interrupts and, hopefully, will just read the pins on the 23017 and should reveal which pin does what as the encoders are exercised.

For me that should correspond to the config file entries:

#Zynthian Wiring Config

export ZYNTHIAN_WIRING_LAYOUT=“CUSTOM”
export ZYNTHIAN_WIRING_ENCODER_A=“110, 114, 102, 105”
export ZYNTHIAN_WIRING_ENCODER_B=“109, 113, 101, 104”
export ZYNTHIAN_WIRING_SWITCHES=“108, 112, 100, 103”
export ZYNTHIAN_WIRING_MCP23017_I2C_ADDRESS=“0x20”
export ZYNTHIAN_WIRING_MCP23017_INTA_PIN=“27”
export ZYNTHIAN_WIRING_MCP23017_INTB_PIN=“25”

Am I right?

But still Zynthian doesn’t like my wiring:
Dec 28 11:53:35 zynthian startx[1510]: ZynCoder->update_polled_zynswitches(0): Wrong pin number 108 in zynswitch 0!
Dec 28 11:53:35 zynthian startx[1510]: ZynCoder->update_polled_zynswitches(0): Wrong pin number 112 in zynswitch 1!
Dec 28 11:53:35 zynthian startx[1510]: ZynCoder->update_polled_zynswitches(0): Wrong pin number 108 in zynswitch 0!
Dec 28 11:53:35 zynthian startx[1510]: ZynCoder->update_polled_zynswitches(0): Wrong pin number 112 in zynswitch 1!