Welcome to the Pycom forum Forum
    • \[\[global:header.categories\]\]
    • \[\[global:header.recent\]\]
    • \[\[global:header.tags\]\]
    • \[\[global:header.popular\]\]
    • Login

    Explore Pybytes | Official Documentation | Report a Firmware Bug/Issue | GitHub

    Does pulses_get function have a 200 bit limitation when reading?

    Scheduled Pinned Locked Moved Discussion
    49 Posts 5 Posters 5.7k Views 5 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • robert-hhR Offline
      robert-hh @Pesien
      last edited by robert-hh

      @Pesien Long long ago i made a UART read by pit probing. As far as I recall, the software:

      • waited for the start bit transition, and then
      • probed the value in the middle of the following bit frames.

      I see no reason why your approach would deviate over time. Expept that the time for append increases. So for the 7 bits of a byte I would preallocate a bytearray of 7 bytes, or just shift that into a single int value. Something like:

              EOL = const(0x0a) # end of line
              self.lista_bytes=b''
              
              while True:
                  bits = 0
                  while True: # wait for the start bit
                      if self.pin_unico()==1:
                          break
                  time.sleep_us(1200) # wait for a 1.5 bit times
              
                  for bit in range (8): # get the next 8 bits
                      bits = (bits << 1) | (1 - self.pin_unico())
                      time.sleep_us(800) # to be determined, about a bit time
                  bits >>= 1
                  self.lista_bytes += bits.to_bytes(1, "little")
                  if bits == EOL: # maybe \n or 0x0a
                      break
      # now msg contains a list of bytes with the response
      

      Edit: If the lowest order bit is sent fist, then the inner loop may be:

                  for bit in range (8): # get the next 8 bits
                      bits = (bits >> 1) | ((1 - (self.pin_unico()) << 8)
                      time.sleep_us(800) # to be determined, about a bit time
      

      and the shift after the loop can be omitted.

      P 1 Reply Last reply Reply Quote 0
      • P Offline
        Pesien @robert-hh
        last edited by

        @robert-hh Thank you. I'll try to preallocate an array of bytes before reading and the 1.5 initial bit time and will update with the results.

        robert-hhR 1 Reply Last reply Reply Quote 0
        • robert-hhR Offline
          robert-hh @Pesien
          last edited by robert-hh

          @Pesien Yesterday evening I though I coudl try if my test function works and indeed got it working for 1200 baud, 7 data bits. What cause me some time to overcome was a glitch in the firmware, which I will analyze later: you cannot have reliable sleep_us(x) with x > 1000, means, sleep_us() longer than 1 ms. The resulting sleep time is sometimes much shorter. The code below uses a low for mark, when reading form the pin. If you want to adapt it, you have to invert the level. At the moment it also generates reference pulses at P11 to see, if the read timing matches the data stream.
          In the target, you would pre-allocate buffer.
          Still, using the built-in UARt class with the method discussed below seems more robust to me.

          from time import sleep_us, sleep_ms
          import gc
          from machine import Pin, disable_irq, enable_irq
          t = Pin("P11", Pin.OUT)
          t(1)
          pin = Pin("P9", Pin.IN)
          
          
          def read_ser(pin_unico):
              EOL = const(0x0d)  # maybe \n or \r
              buffer = bytearray(100)
              buffer_size = len(buffer)
              bytes = 0
              gc.collect()
          
              while True:
                  bits = 0
                  while True:  # wait for the start bit
                      if pin_unico() == 0:
                          break
                  sleep_us(390)  # wait for a 1.5 bit times
                  sleep_us(390)  # since there is a bug for wait time > 1 ms
                  sleep_us(390)  # three shorter sleeps are used.
          
                  for bit in range(7):  # get the next 7 bits
                      bits = (bits >> 1) | (pin_unico() << 7)
                      t(0)
                      sleep_us(20)
                      t(1)
                      sleep_us(780)  # to be determined, about a bit time
                  bits >>= 1
                  bits &= 0x7f
                  # print(hex(bits), chr(bits))
                  buffer[bytes] = bits
                  bytes += 1
                  if bits == EOL or bytes >= buffer_size:
                      return buffer[:bytes]
          # now msg contains a list of bytes with the response
          
          P 1 Reply Last reply Reply Quote 0
          • P Offline
            Pesien @robert-hh
            last edited by

            @robert-hh I'm afraid I am not able to make your code work. The SDI-12 sensors have 10 bits/byte, and the 7 data bits are in inverted USASCII code. You also have the starting high bit, the parity bit and the (low) ending bit.
            Trying to adapt your code to these settings has failed, probably because of my own inexperience and the fact that I don't know what some of the things there do, but it might be because of the inverted 7 data bits.
            I'll keep trying.

            robert-hhR 2 Replies Last reply Reply Quote 0
            • robert-hhR Offline
              robert-hh @Pesien
              last edited by

              @Pesien The 10 bits are 1 start, 7 data, 1 parity, 1 stop.
              my code ignores the start bit (the initial delay skips it) and the parity & stop bit. The latter should always be ignored, to get more time to store the data. Adapting to the reverse polarity and parity should be like the code below. But I will try this evening with reverse polarity.

              from time import sleep_us, sleep_ms
              import gc
              from machine import Pin, disable_irq, enable_irq
              t = Pin("P11", Pin.OUT)
              t(1)
              pin = Pin("P9", Pin.IN)
              
              
              def read_ser(pin_unico):
                  EOL = const(0x0d)  # maybe \n or \r
                  buffer = bytearray(100)
                  buffer_size = len(buffer)
                  bytes = 0
                  gc.collect()
              
                  while True:
                      bits = 0
                      while True:  # wait for the start bit
                          if pin_unico() == 1:
                              break
                      sleep_us(390)  # wait for a 1.5 bit times
                      sleep_us(390)  # since there is a bug for wait time > 1 ms
                      sleep_us(390)  # three shorter sleeps are used.
              
                      for bit in range(8):  # get the next 8 bits including parity
                          bits = (bits >> 1) | ((1 - pin_unico()) << 8)
                          t(0)
                          sleep_us(20)
                          t(1)
                          sleep_us(780)  # to be determined, about a bit time
                      bits >>= 1
                      bits &= 0x7f # strip off parity
                      # print(hex(bits), chr(bits))
                      buffer[bytes] = bits
                      bytes += 1
                      if bits == EOL or bytes >= buffer_size:
                          return buffer[:bytes]
              # now msg contains a list of bytes with the response
              
              1 Reply Last reply Reply Quote 0
              • robert-hhR Offline
                robert-hh @Pesien
                last edited by

                @Pesien I wonder why you tell that the data is inverted. From the SDI12 spec I cannot tell that it is. But besides that, the spec is not very detailed about that topic, and I have no real unit for testing.

                P 1 Reply Last reply Reply Quote 0
                • P Offline
                  Pesien @robert-hh
                  last edited by Pesien

                  @robert-hh I have seen it with an oscilloscope. The sensor returns the data as I said in my last post.
                  Edit: In order to translate the pulses into characters, I created my own dictionary with all the characters, so translation is not needed, only pulse storage.

                  robert-hhR 1 Reply Last reply Reply Quote 0
                  • robert-hhR Offline
                    robert-hh @Pesien
                    last edited by

                    @Pesien I found another spec which confirma that. It had a more detailed timing diagram. OK. That explains why you cannot use the standard UART. It would need an automatic bidirectional inverting driver.

                    P 1 Reply Last reply Reply Quote 0
                    • P Offline
                      Pesien @robert-hh
                      last edited by

                      @robert-hh The thing is that I had it working in two different ways, but both have some kind of limitation. Using the pulses_get function was perfect and robust, but it had the 128 entry limit. The timers worked fine but only the first 6-7 times.
                      I also tried to adapt my own code with the timers to wait more time and to preallocate memory but failed. The timing solution seems the only one for me, if I can make it work...

                      robert-hhR 2 Replies Last reply Reply Quote 0
                      • robert-hhR Offline
                        robert-hh @Pesien
                        last edited by

                        @Pesien I'll checkl this night. I am confident that I g´can get the bit-bang version working,but it will always due to fail. If during receving there is an interrupt, the timing will be disturbed. Using a timer interrupt for the bit probing of a byte is also no reliable solution, since the PyCom devices have a largely varying ISR-response latency, reaching from 10 to 900 µs.

                        1 Reply Last reply Reply Quote 0
                        • robert-hhR Offline
                          robert-hh @Pesien
                          last edited by

                          @Pesien Reading through the data sheet and API of the ESP32 I noticed, that the ESP32 UART can operate with inverted signals. This may not be supported by the Micropython API, but it should not be too difficult to add. I'll try that too. If that works, the UART can be used.

                          P 1 Reply Last reply Reply Quote 0
                          • P Offline
                            Pesien @robert-hh
                            last edited by

                            @robert-hh My teacher has it working with the UART and two pins. Since my project is to create an UART I don't think I can use it anyways.

                            robert-hhR 2 Replies Last reply Reply Quote 0
                            • robert-hhR Offline
                              robert-hh @Pesien
                              last edited by

                              @Pesien With two pins and the UART, you can use external inverters. With just one pin, you have to switch the UART to inverted mode. But I understand your task.

                              1 Reply Last reply Reply Quote 0
                              • robert-hhR Offline
                                robert-hh @Pesien
                                last edited by robert-hh

                                @Pesien This is the code for the inverted data, which also reads and ignores the parity bit. It reads from P9 and still has the timing marks on P11. If you drop these, you have to add a few (~4) µs to the loop's sleep_us. It is working here on a LoPy4. It returns a bytearray. If you need a string, you can convert that with bytes(return_value).decode()

                                from time import sleep_us, sleep_ms
                                import gc
                                from machine import Pin
                                t = Pin("P11", Pin.OUT)
                                t(1)
                                pin = Pin("P9", Pin.IN)
                                
                                
                                def read_ser(pin_unico):
                                    EOL = const(0x0d)  # maybe \n or \r
                                    buffer = bytearray(100)
                                    buffer_size = len(buffer)
                                    bytes = 0
                                    gc.collect()
                                
                                    while True:
                                        bits = 0
                                        while True:  # wait for the start bit
                                            if pin_unico() == 1:
                                                break
                                        sleep_us(390)  # wait for a 1.5 bit times
                                        sleep_us(390)  # since there is a bug for wait time > 1 ms
                                        sleep_us(390)  # three shorter sleeps are used.
                                
                                        for bit in range(8):  # get the next 8 bits
                                            t(0) # sync-marker
                                            bits = (bits >> 1) | ((1 - pin_unico()) << 7)
                                            t(1)
                                            sleep_us(810)  # to be determined, about a bit time
                                        bits &= 0x7f  # mask 7 bit
                                        buffer[bytes] = bits
                                        # print(hex(bits), chr(bits))
                                        bytes += 1
                                        if bits == EOL or bytes >= buffer_size:
                                            return buffer[:bytes]
                                # now buffer contains a list of bytes with the response
                                

                                I called it a few times in a row with supplying a ~100 charactes line, and that is the result. Obviously, if you feed lines too fast, it gets out of sync:

                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                bytearray(b'Far away behind the word mountains, far from Vokalia and Consonantia, there live the blind texts.\r')
                                

                                Below is a logic analyser snap shot of a read, which shows, that the read markers match the bit location centers.
                                Read serial inverted.jpg

                                P 2 Replies Last reply Reply Quote 0
                                • P Offline
                                  Pesien @robert-hh
                                  last edited by

                                  @robert-hh Thank you again for all your help. I'll try it tomorrow and see if it works finally.

                                  robert-hhR 2 Replies Last reply Reply Quote 1
                                  • robert-hhR Offline
                                    robert-hh @Pesien
                                    last edited by

                                    @Pesien Just for interest I tried to set up an interrupt based version where:

                                    • the start bit would trigger a pin interrupt callback, which then
                                    • fires a series of n timer callbacks to read the bits of each byte.

                                    Even if the software itself worked, that attempt failed because:

                                    • the callback latency of the pin interrupt is greatly varying between 100µs and ~1000µs, making it impossible to sync on the first bit.
                                    • the shortest period of a sequence of timer interrupts is about 2 ms. 0.883 ms would be needed for 1200 baud.

                                    Even if the timer based bit probing is replaced by the active waiting method, the initial pin interrupt latency prevents reliable reading.

                                    1 Reply Last reply Reply Quote 1
                                    • robert-hhR Offline
                                      robert-hh @Pesien
                                      last edited by robert-hh

                                      @Pesien I tried this interrupt mechanism with the microypthon.org branch of MicroPython. That works better. Only the first character in a message is disturbed after a longer (few seconds) break between messages. This glitch is caused by the timer burst delayed by ~500µs. The reason for that should be the relevant code not being in the memory cache and having to be imported from serial flash. This code page fetch takes at least 300µs.

                                      1 Reply Last reply Reply Quote 0
                                      • P Offline
                                        Pesien @robert-hh
                                        last edited by Pesien

                                        @robert-hh Finally I got some time to work on this. I tried your solution, but for some reason it simply does not work.
                                        However, I managed to get it working with the next code:

                                        def read(self):
                                        
                                                
                                                self.pin_unico=Pin(Pin_SDI12, mode=Pin.IN, pull=Pin.PULL_DOWN)
                                                self.lista_bits=[0,0,0,0,0,0,0,0,0,0]
                                                self.lista_bytes=[None]*50
                                                y=0
                                        
                                        
                                        
                                                while True:
                                                    while True:
                                                        if self.pin_unico()==1:
                                                            break
                                                    time.sleep_us(390)
                                                    time.sleep_us(390)
                                                    time.sleep_us(390)
                                                    self.lista_bits[0]=1
                                        
                                        
                                                    for x in range (0, 8):
                                                        self.lista_bits[x+1] = self.pin_unico()
                                                        time.sleep_us(814)
                                        
                                        
                                                    self.lista_bits[9]=0
                                                    #print(self.lista_bits)
                                        
                                                    if self.lista_bits==[1,1,0,1,0,1,1,1,1,0]:
                                                        self.lista_bytes[y]=self.lista_bits
                                                        self.lista_bits=[0,0,0,0,0,0,0,0,0,0]
                                                        break
                                                    else:
                                                        self.lista_bytes[y]=self.lista_bits
                                                        self.lista_bits=[0,0,0,0,0,0,0,0,0,0]
                                                        y+=1
                                                        #print(self.lista_bytes)
                                        
                                                #print(self.lista_bytes)
                                                
                                                return(self.lista_bytes)
                                        

                                        The thing is that it only works the first time (first byte) and it gets stucked here the second time:

                                        while True:
                                            if self.pin_unico()==1:
                                                break
                                        

                                        I don't know why this is happening. It makes no sense to me.
                                        Edit: I just tried it and your code has the same issue but I cant make it work even the first time.

                                        robert-hhR 2 Replies Last reply Reply Quote 0
                                        • robert-hhR Offline
                                          robert-hh @Pesien
                                          last edited by

                                          @Pesien The timing is essential. You have to wait for the next byte in the time frame between the last bit of the previous byte and the start bit of the next byte. That time frame may be as short a one bit duration (883 µs). If you miss the transision of the start bit, all timing is disturbed for a byte and all subsequent bytes. The code you have at the end of a byte seems to be too slow.
                                          Yind you have an oscilloscope, you can try to monitor the state of your code in relation to the incoming data, liek I did with the monitor pulses. A sequence debug_pin(1);debug_pin(0) takes about 5 µs and will leave a visible time marking in the oscilloscope. Without such analysis capabilities it's hard to get along. I'm using a check logic analyzer tool for that, like this one: https://www.amazon.com/HiLetgo-Analyzer-Ferrite-Channel-Arduino/dp/B077LSG5P2/ref=sr_1_3?crid=3S1O51OGJW1Q2&keywords=logic+analyzer+usb&qid=1558955763&s=gateway&sprefix=logic+anal%2Caps%2C242&sr=8-3, which works nice together with the saleae logic analyzer software.

                                          P 1 Reply Last reply Reply Quote 0
                                          • robert-hhR Offline
                                            robert-hh @Pesien
                                            last edited by

                                            @Pesien said in Does pulses_get function have a 200 bit limitation when reading?:

                                            The other thing is about how pythone deals with objects like a list. The statement self.lista_bytes[y]=self.lista_bits does not copy the list, but just copies a reference. If you change lista_bits later on, the entry in lista_bytes will be changed too. At the end, all entries in lista_bytes will be the same. So instead of self.lista_bytes[y]=self.lista_bits, you must write:
                                            self.lista_bytes[y]=self.lista_bits[:], which will create a copy. In any case self.lista_bits=[0,0,0,0,0,0,0,0,0,0] is not needed.

                                            1 Reply Last reply Reply Quote 0

                                            Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                                            Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                                            With your input, this post could be even better 💗

                                            Register Login
                                            • 1
                                            • 2
                                            • 3
                                            • 2 / 3
                                            • First post
                                              Last post

                                            Pycom on Twitter

                                            Pycom