Found problem with GPIO tutorial's python code.

Hi Renesas Support Team,

I found a few problems with LED toggle python code (for Medium One cloud) in GPIO tutorial.    I would like to discuss with you and confirm it.  I also have fixed the code and present a working code. 

The problems are:

1. LED will toggle every 1 minute regardless of the interrupt pin pressed or not.

2. The de-bounce code is always ignored.  It is never used.

3. In the de-bounce code, Last 2 interrupt events has no 'observed_at' time.   Syntax error occurs when the code tries to determine the time difference between timer trigger time and the interrupt event time.

Best regards,

Michael

 

 

Python code with explanation why these 3 problems happen.


in1 = IONode.get_input('in1')    # 1 min timer trigger input

if IONode.is_trigger('in1'):   # According to Doc, this is always false.  (the cause of problem #2)

                            # raw.interrupt trigger is not connected to python base module.  The code does not depend on the interrupt pin state.  (the cause of problem #1)

    last_2_interrupts = Analytics.last_n_values('raw.interrupt', 2)    # This function does not include "observed at" value.   (the cause of problem #3)

        if len(last_2_interrupts) > 1:

            if DateConversion.to_py_datetime(in1['observed_at']) - DateConversion.to_py_datetime(last_2_interrupts[1]['observed_at']) < datetime.timedelta(seconds=1):      # syntax error!   'observed_at' type cannot be found.
                    escape()

try:  # Always execute because if IONode.is_trigger('in1') is always false. 

    prev_state = int(Store.get('prev_state')) 

except (ValueError, TypeError):

   prev_state = 0

# toggle LED state

next_state = 0 if prev_state else 1

write_pin(0, next_state)

Store.set_data('prev_state', str(next_state))

  • Here is my solution.

    1. Connect raw.interrupt to Python Base Module as 'in2'

    2. Fix the code.

    import MQTT

    import Store

    import Analytics

    import DateConversion

    import datetime

    import Filter

    def read_pin(pin, tag):

       send_mqtt_msg(u'0;4;{};{};'.format(pin, tag));

    def write_pin(pin, val):

       send_mqtt_msg(u'0;3;{};{};'.format(pin, val));

    def send_mqtt_msg(msg):

       mqtt_msg = msg

       if not isinstance(msg, basestring):

           mqtt_msg = ''.join(map(unichr, msg))

       MQTT.publish_event_to_client('s3a7', mqtt_msg, 'latin1')

    # debounce

    log ("Detect an input trigger.  It could be a timer or interrupt trigger.")

    in1 = IONode.get_input('in1')

    log (in1)

    in2 = IONode.get_input('in2')

    log (in2)

    if IONode.is_trigger('in2'):

       log("in2 input trigger is true.")

       last_2_interrupts = Analytics.last_n_values('raw.interrupt', 2)

       log ('Length of the list:')

       log (len(last_2_interrupts))

       log ('print last 2 interrupt data values')

       for e in last_2_interrupts:

           log (e)

       if len(last_2_interrupts) > 1:    

           last_2_interrupt_events = Analytics.events(stream_name='raw', filters=Filter.string_tag('raw.interrupt'), limit=2, sort=('observed_at', 'DESC'))

           log ('Length of the interrupt event list:')

           log (len(last_2_interrupt_events))

           log ('print last 2 interrupt events')

           for e in last_2_interrupt_events:

              log (e)

           log ("print last 2 interrupt events' observed_at time")

           for e in last_2_interrupt_events:

                log (e.get('observed_at'))

           #log (last_2_interrupt_events[0]['observed_at'])  

           #log (last_2_interrupt_events[1]['observed_at'])  

           log ('Print in1 observed at {0}'.format(in1['observed_at']))

           log ('Print in2 observed at {0}'.format(in2['observed_at']))

           timediff = DateConversion.to_py_datetime(in1['observed_at']) - DateConversion.to_py_datetime(last_2_interrupt_events[1]['observed_at'])

           log ("Time Difference between in1(timer1) and last_2_interrupt_events[1] is {0} ".format(timediff))

           if DateConversion.to_py_datetime(in1['observed_at']) - DateConversion.to_py_datetime(last_2_interrupt_events[1]['observed_at']) < datetime.timedelta(seconds=1):

               log ('Difference in time between the timer trigger and the last interrupt trigger is less than 1 sec.')

               log ('Escape to skip the rest of the code.')

               escape()

           else:

               log ('Proceed to toggle the LED state.')

    else:

       log("in2 input trigger is false.")

       log ('Escape to skip the rest of the code.')

       escape()

    log ("Execute try")

    try:

       prev_state = int(Store.get('prev_state'))

    except (ValueError, TypeError):

       log ("prev_state = None. Initialize prev_state to zero.")

       prev_state = 0

    log ("prev_state : {0}".format(prev_state))

    # toggle LED state

    next_state = 0 if prev_state else 1

    write_pin(0, next_state)

    Store.set_data('prev_state', str(next_state))

  • In reply to Mike:

    Here is the log summary. (I added a lot of log functions to verify the code and learn how the code works.)

    "logs": "Detect an input trigger. It could be a timer or interrupt trigger.
    {u'event_data': {}, u'observed_at': u'2017-05-08T23:09:07.207999945Z'}
    {u'tag_name': u'raw.interrupt', u'event_data': {u'value': 9}, u'trigger': True, u'observed_at': u'2017-05-08T23:09:07.207999945Z'}
    in2 input trigger is true.
    Length of the list:
    2
    print last 2 interrupt data values
    {'raw.interrupt': 9}
    {'raw.interrupt': 9}
    Length of the interrupt event list:
    2
    print last 2 interrupt events
    {u'event_data': {u'device_id': u's3a7', u'interrupt': 9}, u'observed_at': u'2017-05-08T23:09:07.208000+00:00', u'stream_id': 408578679221035008L, u'user': u'device'}
    {u'event_data': {u'device_id': u's3a7', u'interrupt': 9}, u'observed_at': u'2017-05-08T23:05:53.130000+00:00', u'stream_id': 408578679221035008L, u'user': u'device'}
    print last 2 interrupt events' observed_at time
    2017-05-08T23:09:07.208000+00:00
    2017-05-08T23:05:53.130000+00:00
    Print in1 observed at 2017-05-08T23:09:07.207999945Z
    Print in2 observed at 2017-05-08T23:09:07.207999945Z
    Time Difference between in1(timer1) and last_2_interrupt_events[1] is 0:03:14.077000
    Proceed to toggle the LED state.
    Execute try
    prev_state : 0
  • In reply to Mike:

    Here is what the workflow looks like (debug is enabled).

  • In reply to Mike:

    Hi Support Team,

    Actually my code is only working 80% right as I tested more today. So, I removed the timer completely from the workflow. I changed the code to check the time difference between the previous and current interrupt event time. It still gives me a somewhat mixed result.

    The main problem I observed is that the firmware on IOT board is not behaving consistently. I wrote some new codes to perform reading from as well as writing to GPIO pins. LED turning on/off is not responsive for the write command. "{GPIO#: value} is not seen consistently for the read command. This type of problem will never appear if I don't press the interrupt button and use the 1 minute timer to send the read or write command every 1 minute. Once a while I need to reset the IOT board to get working again if I press the interrupt button down too long.

    I am a hardware engineer. I think that the CPU Interrupt input is usually synchronized with the system clock and must remain asserted for # of clock cycles for proper CPU operations. I probed the button input with the logic analyzer. There are a lot of bounces/toggles (5~10) on both edges of the negative interrupt pulse generated by the button press. The edges are very likely not in sync with the clock.

    Is there synchronization circuit in CPU to sync an asynchronous signal to prevent metastable condition in internal flip flops? Could the button bouncing violate some basic hardware timing requirement?

    It is hard to debug. Making a better python code on debounce does not seem to help. Hope this new information helpful.

    Best regards,
    Michael

    PS: In spite of the challenge, it is a very nice tutorial. It makes me think harder and help me understand more. Writing and reading GPIO pins is very useful. I enjoy reading all the tutorials on Renesas!

     

    Logic Analyzer Picture (Bounces)

  • In reply to Mike:

    I started to read the SSP 1.1.0 Manual. I did not realize that there is real industry strength OS and support for GPIO in the firmware. Maybe my view in the last reply is too simplistic. I appreciate how the tutorial guides us. The real RTOS code is very sophisticated under the hood. I am going to get familiar with the device, API ... etc.

    Best,
    Michael
  • In reply to Mike:

    Hi Mike,

    Thank you for your feedback. Yes, you are correct. Regarding #2, an incorrect picture of the workflow connections was posted; the correct version connects the Tag node input "raw:interrupt" to input "in1" of the Base Python node, instead of the Custom scheduler node. The blog post has been updated to show this corrected image.

    Regarding #3, the blog post has been corrected to retrieve the last 2 events using the Analytics.events() API, based on what you have shown.

    Regarding #1, the intent of this blog is to have the LED toggling every minute, in addition to toggling when an interrupt is received, as described in section 14 of the blog.

    Sorry for the inconvenience caused by these issues. Please let us know if you have any further feedback.


    Thanks!
  • In reply to Lakshan:

    Hi Lakshan,

    Thank you for your kind response and quick blog update. I can see the update already made in the blog. Fantastic! I appreciate your help. I see. The 1 minute timer is intentional. Good.

    No problem for the inconvenience at all. It motivates me to learn and study the code deeper.

    Yes, I do have more needs with GPIO tutorial. I have trouble reading the GPIO pins in the input mode. I created a new post. Would you be able to help me?

    I tried to debug. I don't understand how Renesas pack the command to the IOT board for mqtt message. I tried to find a document on mqtt message format because I see it here and I see it everywhere for sending read/write commands to i2c and spi peripheral devices. Would you have such information and document on mqtt message format spec? I like to have the information.

    def read_pin(pin, tag):
    send_mqtt_msg(u'0;4;{};{};'.format(pin, tag)); # 0, 4, pin, tag where pin is a number and tag is a string like "GPIO15"

    def write_pin(pin, val):
    send_mqtt_msg(u'0;3;{};{};'.format(pin, val)); # 0, 3, pin, val where pin is a number and val is a number (only 0 or 1
    should be acceptable.)

    How does the field with the value of 4 and 3 mean? I tried to find the c code in the IOT firmware that interprets these values, so I could find the cause why I cannot read the GPIO pins.   Where in the C code in SmartChef firmware the mqtt message is processed?

    Thank you,
    Michael