What's up with the Signal class?

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
Post Reply
srogers
Posts: 11
Joined: Sun Feb 06, 2022 12:25 am

What's up with the Signal class?

Post by srogers » Mon Mar 07, 2022 5:46 am

OK, my experience with the Signal class is only with MicroPython 1.18 for the RP2 (Pico Pi). I looked at the Signal class for simplifying a de-bounce circuit that I have made, which currently only works for Pin inputs that are pulled up. I want to integrate the code that I have, which is also integrated with Pin.irq() to handle servicing the Pin with an interrupt.

I'm not that much up on classes in Python, but it appears that the Signal class either "has a" Pin class, or "is a" Pin class. so I constricted a Signal class like this.

Code: Select all

g_k1_signal = Signal(15, Pin.IN, Pin.PULL_UP, invert=True)
At this point, I wanted to execute the following code to associate an IRQ with the Signal so that I could handle it in an IRQ handler:

Code: Select all

g_k1_signal.irq(handler = bouncer_handler, trigger = Pin.IRQ_FALLING
At this point, the error that I got was that g_k1_signal did not have an attribute 'irq'. As I read the documentation on the Signal class, it was my understanding that the Signal class would "have a" Pin or would "be a" Pin class. When i did the following in REPL, I got the following information:

Code: Select all

MicroPython v1.18 on 2022-01-17; Raspberry Pi Pico with RP2040

Type "help()" for more information.
>>> from machine import Pin, Signal
>>> help (Signal)
object <class 'Signal'> is of type type
  value -- <function>
  on -- <function>
  off -- <function>
>>> 
The only Pin attributes exposed by the Signal class consist of value(), on() and off(). It would have been nice to keep the name of the Signal class 'g_k1_signal' for the name associated with the IRQ handler. As you can see, the underlying PIN of the Signal class does have an attribute of irq().

Code: Select all

help(Pin)
object <class 'Pin'> is of type type
  init -- <function>
  value -- <function>
  low -- <function>
  high -- <function>
  off -- <function>
  on -- <function>
  toggle -- <function>
  irq -- <function>
  IN -- 0
  OUT -- 1
  OPEN_DRAIN -- 2
  ALT -- 3
  PULL_UP -- 1
  PULL_DOWN -- 2
  IRQ_RISING -- 8
  IRQ_FALLING -- 4
  
I just wonder why the Signal class does not handle the irq() attribute of the underlying Pin class. This lack of propagation for the irq() attribute, from what I can see, does a pretty good job of wrecking the utility of the Signal class.

Shards
Posts: 39
Joined: Fri Jun 25, 2021 5:14 pm
Location: Milton Keynes, UK

Re: What's up with the Signal class?

Post by Shards » Tue Mar 08, 2022 7:01 am

The simple answer is that Signal is not a subclass of Pin so Signal doesn't inherit Pin's methods. Signal uses a Pin object to create an object to optionally invert the logic of a Pin and nothing else.

If you need to do anything with PIn except turn things on and off You are better sticking with Pin.

srogers
Posts: 11
Joined: Sun Feb 06, 2022 12:25 am

Re: What's up with the Signal class?

Post by srogers » Sat Mar 12, 2022 12:22 am

OK, this post is tied to my original post about my dissatisfaction with the Signal class. The Signal class 'tries' to be helpful by providing what appears to look like a super-class of Pin with the ability to change the sense of the pins status, based on whether the pin is a PULL_UP or PULL_DOWN input pin, or on whether the pin is an output pin active as a source (HI) or as a sink (LOW). Yhe Signal class offers the capability of initializing the underlying Pin class, which I think is quite good.

So I tried using it, under the impression that a Signal class "is a" Pin class. As I tried to use the Signal class, I wanted to incorporate the notion of accessing the irq attribute of the Pin class through the Signal class, but for some reason, the Pin class' irq attribute is not accessible through the Signal class, even though it is accessible via the Pin class.

Then I read Shard's post about Signal not being a subclass of Pin, which it obviously is not. So I set about to make my own Signal class, which would incorporate all the features I wanted, and which seemed to be a rather simple thing to try. And two days later, I have been unable to get what I wanted as a SignalPin class.

My design for a signal class took on the name of SignalPin. The design of SignalPin class was to inherit SignalPin from the stock Pin class. Now I have done class design, including class design with C++ and C#, but I never expected how difficult this was to become.

One of the sticking points that my design has consists of the packing issues around the parameters of the Pin class.

The constructor for the Pin class has the following parameters:

Code: Select all

class machine.Pin(id, mode=- 1, pull=- 1, *, value=None, drive=0, alt=- 1) 
From what I can gather, the asterisk in the parameter list indicates a separation between the preceding parameters, which are positional only, and the following parameters, which are keyword only.

I decided that the constructor of the SignalPin class I was designing should be:

Code: Select all

class SignalPin(id, mode = -1, pull = -1, invert = False, value = None)
Note that I dropped the drive and alt parameters, and I dropped the * designator in the parameter list. I only have one parameter in the keyword only parameter list of PinSignal because I don't expect to be using the the other parameters, and I have the expectation that these missing parameters will be handled by their defaults in the Pin class.

So here is my simple case code for testing PinSignal as an input:

Code: Select all

from machine import Pin
import time

class PinSignal(machine.Pin):
    
    def __init__(self, id, mode = -1, pull = -1, invert = False, value = None):
        Pin(id)
        self.invert = invert
        
        if (value == None):
            Pin.init(mode, pull)                    # line 11 error location
        else:
            Pin.init(mode, pull, value if ~self.invert else ~value)
        
    def value(self, val):
        if (val == None):
            if (self.invert):
                return ~self.Pin.value()
            else:
                return self.Pin.value()
        elif self.invert:
            self.Pin.value(~val)
        else:
            self.Pin.value(val)
            
    def on(self):
        value(self, True)
            
            
    def off(self):
         value(self, False)
            
       
            
g_pin_k1 = PinSignal(id = 15, mode = Pin.IN, pull = Pin.PULL_UP, invert = True, value = None)

while True:
    print (g_pin_k1)
    time.sleep(1)

My problems right now consist of errors like this:

Code: Select all

Traceback (most recent call last):
  File "<stdin>", line 35, in <module>
  File "<stdin>", line 11, in __init__
TypeError: argument has wrong type
I have a few confusions.
1. Do the parameter packing hints cause problems between parameter locations in the super class
and the base class?
2. Am I correctly handling the transfer of the Pin parameters from the PinSignal parameters?
3. If I remove the packing information from parameters in the PinSignal class, does this cause
a problem in the Pin subclass?
4. How do I differentiate between call to value(), meaning return the value, and value(True) or
value(False).
5. How complex can it be to do something so seemingly simple?

User avatar
karfas
Posts: 193
Joined: Sat Jan 16, 2021 12:53 pm
Location: Vienna, Austria

Re: What's up with the Signal class?

Post by karfas » Sat Mar 12, 2022 10:41 am

I think you should try to learn a little about python. The time you used for the rant about the Signal class and your own implementation might have been used to read a few articles and understand what you are doing.

Google is most likely a way better resource regarding basic python constructs than the micropython forum.
e.g. "python function argument list" => (first entry) https://levelup.gitconnected.com/5-type ... e2a2cafd29
or "python subclass init" => (second hit) => https://realpython.com/python-super/

The errors I see in your PinSignal code on the first sight (someone else please correct me if I'm wrong, I have myself way too less experience with certain python constructs):
- in __init__, you call Pin(...). As far as I know, this will create a Pin object (and throw it away, as you don't dare to save the object to a variable). super().__init__(...) is maybe what you want.
- Line 11: I have no idea what Pin.init() does in this context and the parameter you require for it. The base class init function is super().__init__().
- self.Pin.value() calls: Have also no idea if this is correct. I would use super().value().
A few hours of debugging might save you from minutes of reading the documentation! :D
My repositories: https://github.com/karfas

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: What's up with the Signal class?

Post by pythoncoder » Sat Mar 12, 2022 1:06 pm

To add to the above, MicroPython has only limited support of subclassing of built-in types. See this doc:
Subclassing of builtin types is partially implemented, but there may be various differences and compatibility issues with CPython.
In my experience it's best avoided: use composition rather than inheritance.

The Signal class is (in my opinion) mainly an aid to beginners unfamiliar with hardware and software logic. I wouldn't expect advanced functionality from it.
Peter Hinch
Index to my micropython libraries.

Post Reply