Display-o-Tron HAT addressing


#1

Display-o-Tron is working perfectly on a Pi3+.
Piface 2 Digital io relay board is working perfectly on a Pi3+
When they are both connected the Display-o-Tron display is not updated, only the cursor flashes.
They keypad responds perfectly.
I have tried changing the hardware addressing on the PiFace but this has had no effect.

Someone much cleverer than I suggested it might be possible to change SPI I2C control addresses but I have no idea how to do that.

Any suggestions welcome.


#2

The display o tron hat pinout is here, https://pinout.xyz/pinout/display_o_tron_hat# if that helps. I couldn’t find any decent info on the Piface though? Other than price and availability.


#3

Thanks but I already know that there is a pin incompatibility. I need to know if it is possible to change the address assignments and if so how to do it. I can find nothing about this in any Pimoroni docs and I know that the PiFace addresses cannnot be reassigned. :(


#4

Probably not? Usually, if the i2c address is changeable with say a jumper, its mentioned in the documentation and or description. The display o tron uses two i2c addresses. You’ll probably find those are the i2c address for the chip used to do that function.
That’s one drawback to this type of stuff. The official Pi Foundation Hat standard, dictates that only one Hat can be used. If you follow the standard and use the eprom with the HAT ID. If they don’t have the eprom they aren’t technically a “HAT” but you can then use more than one. I think that’s why a lot don’t use the eprom.
Even then though, you can end up in a situation where addresses collide. That’s the way things go some times. Adding the option to change the i2c address adss extra cost etc. If its even doable and you have space on the board to do it.


#5

The display on Display-o-Tron HAT uses SPI- which does not support addressing, but rather uses chip-select pins. It’s likely you’ve encountered a pin conflict that is not possible to fix without compromising functions from one HAT or the other, or manually re-assigning pins. I don’t have any information on the Pi Face Digital pinout, though, so I couldn’t venture what the problem might be.


#6

Thanks for all the info/help. I have now purchased a USB relay and a I2C relay with programmable address which should bypass the address issue.


#7

I’m making progress but have a related question.
If I call a subroutine keys are no longer responded to.
Does that mean that I cannot use subroutines and still recognise that a “cancel” key has been pressed to exit the routine?
Or do I have to somehow pass the key value, which has not yet been pressed, to the subroutine that is being called?
Or do I have to bring my subroutine into the key loop?


#8

You’ll have to post some minimal example code, because I’m struggling to grasp your problem. Is this in Python?

Generally the only thing you should ever do in a button press handler is set some global state which is then read and acted upon by your main loop and - potentially - any threads you have running.


#9

Thanks got it working!
Yes Python.


#10

Ouch! its not working how I want! And not recognising a key press whilst in a loop writing to usb relays.The program is 600 lines. Is it acceptable to post 600 lines to ask for assistance? If so how do I put ‘’’ in front of 600 lines? Sorry to be a pain. I was really pleased with my progress but… my program works great except that I cannot escape from my disstate=4. This would allow me to exit the scanning loop.


#11

OK I’ll try again.
With my program after the TOUCH key is selected it runs a loop which lasts about 5 minutes. During this time is seems impossible to recognise another keypress although the another key is programmed to recognise the state and cancel the loop. Is this so designed? Is there another command which I should be using? I read about a menu plugin. Is this the way I have to go?


#12

I know I’m a few months late- but spawning a thread would be the right way to deal with this. Since input handling on the Dot3k menu library is not automatically threaded, any delay in processing your action will lock up the UI and any further input until it’s finished.


#13

Thanks! Perfect timing. This project has been on hold for various reasons including lack of time to follow up and oddly this weekend I thought that I should take another look at it.
Spawning is new to me. Is there example somewhere on this site that I can use as a model? I searched but couldn’t find one.
I also took a look at Stack Exchange and there seem to be a lot of different techniques which is not what I wanted to see…


#14

That’s par for the course in Python- everyone seems to have their own pattern.

Note: Skip ahead to “Set and forget…” if you don’t need to stop or interact with your long running task

In my case I use a pattern called StoppableThread and another called AsyncWorker which, when combined, give you nice copy and paste classes to handle most of the common use cases for threads- at least until it starts getting very application specific and complex.

They are as follows:

import threading


class StoppableThread(threading.Thread):
    """Basic Stoppable Thread Wrapper
    Adds event for stopping the execution
    loop and exiting cleanly."""
    def __init__(self):
        threading.Thread.__init__(self)
        self.stop_event = threading.Event()
        self.daemon = True

    def start(self):
        if not self.isAlive():
            self.stop_event.clear()
            threading.Thread.start(self)

    def stop(self):
        if self.isAlive():
            self.stop_event.set()
            self.join()


class AsyncWorker(StoppableThread):
    """Basic thread wrapper class for asynchronously running functions
    Return False from the worker function to abort loop."""
    def __init__(self, todo):
        StoppableThread.__init__(self)
        self.todo = todo

    def run(self):
        while not self.stop_event.is_set():
            # Explicitly check for False being returned
            # from worker, IE: Don't allow None
            if self.todo() is False:
                self.stop_event.set()
                break

In your case you might want to rewrite your 5 minute loop to be run by AsyncWorker, which basically involves converting your loop into function which can be run over and over again. This is by no means essential, but it does mean - assuming what you’re doing allows - that you can stop this process before the 5 minute run time is up.

A simple example might be:

count = 0

def count_to_ten():
    global count
    count += 1
    if count == 10:
        return False
    time.sleep(1)

worker = AyncWorker(count_to_ten)
worker.start()

while True:
    print(count)
    time.sleep(0.5)

Technically the AsyncWorker code should probably use while not event.wait(some_sleep_amount) to delay, rather than time.sleep() but this is a minor implementation detail you’ll only run into with more sophisticated uses of threads.

Set and forget…

If you dont want or need to stop your 5 minute task mid process, and if your task is sufficiently detacted from the rest of your code that you’re really just runnign something in the background, you can just spawn a thread and forget about it like so:

import threading
import time

def long_running_task():
    for x in range(100):
        time.sleep(10)

thread = threading.Thread(target=long_running_task)
thread.start()

In all cases your “thread” should have some kind of time.sleep() becuase Python has a “global interpreter lock” that basically only allows one thread to access objects at a time. If you don’t give up the “GIL” manually to give other code a chance to run and interact with the objects it needs, you’ll run into all sorts of exciting problems!


#15

Thanks so much! I finally got round to trying it and I am a happy bunny. HNY to you.