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

    GY-87 micropython library

    Scheduled Pinned Locked Moved Discussion
    micropythongy-80lopy4
    32 Posts 4 Posters 5.2k Views 4 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 @tttadam
      last edited by

      @tttadam A short google search gave an answer:
      The chip in the board is not a HMC5883, but a QMC5883. See https://github.com/nodemcu/nodemcu-firmware/issues/2187
      The QMC5883 server for the same purpose, but has different programming interface.

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

        @tttadam I found a RasPi driver for the QMC5883 and adapted it to pycom MP:

        #
        # ported from http://nobytes.blogspot.com/2018/03/qmc5883-magnetic-field-sensor.html
        #
        
        import time
        import struct
        from machine import idle
        
        class QMC5883:
            #Default I2C address
            ADDR = 0x0D
        
            #QMC5883 = Register = numbers
            X_LSB = 0
            X_MSB = 1
            Y_LSB = 2
            Y_MSB = 3
            Z_LSB = 4
            Z_MSB = 5
            STATUS = 6
            T_LSB = 7
            T_MSB = 8
            CONFIG = 9
            CONFIG2 = 10
            RESET = 11
            CHIP_ID = 13
        
            #Bit values for the STATUS register
            STATUS_DRDY = 1
            STATUS_OVL = 2
            STATUS_DOR = 4
        
            #Oversampling values for the CONFIG register
            MC5883L_CONFIG_OS512 = 0b00000000
            CONFIG_OS256 = 0b01000000
            CONFIG_OS128 = 0b10000000
            CONFIG_OS64  = 0b11000000
        
            #Range values for the CONFIG register
            CONFIG_2GAUSS = 0b00000000
            CONFIG_8GAUSS = 0b00010000
        
            #Rate values for the CONFIG register
            CONFIG_10HZ = 0b00000000
            CONFIG_50HZ = 0b00000100
            CONFIG_100HZ = 0b00001000
            CONFIG_200HZ = 0b00001100
        
            #Mode values for the CONFIG register
            CONFIG_STANDBY = 0b00000000
            CONFIG_CONT = 0b00000001
        
        
            def __init__(self, i2c):
                self.i2c = i2c
                self.oversampling = QMC5883.CONFIG_OS64
                self.range = QMC5883.CONFIG_2GAUSS
                self.rate = QMC5883.CONFIG_100HZ
                self.mode = QMC5883.CONFIG_CONT
                self.reset()
        
            def reconfig(self):
                print("{0:b}".format(self.oversampling | self.range | self.rate | self.mode))
                self.i2c.writeto_mem(QMC5883.ADDR, QMC5883.CONFIG, self.oversampling | self.range | self.rate | self.mode);
        
            def reset(self):
                self.i2c.writeto_mem(QMC5883.ADDR, QMC5883.RESET, 0x01)
                time.sleep(0.1)
                self.reconfig()
                time.sleep(0.01)
        
            def setOversampling(self, x):
                self.oversampling = x
                self.reconfig()
        
            def setRange(x):
                self.range = x
                self.reconfig()
        
            def setSamplingRate(self, x):
                self.rate = x
                self.reconfig()
        
            def ready(self):
                status = self.i2c.readfrom_mem(QMC5883.ADDR, QMC5883.STATUS, 1)[0]
        
                # prevent hanging up here.
                # Happens when reading less bytes then then all 3 axis and will end up in a loop.
                # So, return any data but avoid the loop.
                if (status == QMC5883.STATUS_DOR):
                    print("fail")
                    return QMC5883.STATUS_DRDY
        
                return status & QMC5883.STATUS_DRDY
        
            def readRaw(self):
                while not self.ready():
                    idle()
                    pass
        
                # Python performs a wrong casting at read_i2c_block_data.
                # The filled buffer has to be onverted afterwards by mpdule Struct
                register = self.i2c.readfrom_mem(QMC5883.ADDR, QMC5883.X_LSB, 9);
        
                # Convert the axis values to signed Short before returning
                x, y, z, status, t = struct.unpack('<hhhBh', register)
        
                return (x, y, z, t)
        

        and a short test code:

        import time
        
        from qmc5883 import QMC5883
        
        from machine import I2C
        i2c=I2C()
        i2c.writeto_mem(104, 107, 128) # reset MPU6050
        i2c.writeto_mem(104, 107, 0) # disable sleep
        i2c.writeto_mem(104, 55, 2)   # enable I2C passthru
        
        print(i2c.scan())
        time.sleep(1)
        
        qmc = QMC5883(i2c)
        
        while True:
            print(qmc.readRaw())
            time.sleep(1)
        
        T 2 Replies Last reply Reply Quote 0
        • T Offline
          tttadam @robert-hh
          last edited by

          @robert-hh thank you, it's looks useful, but after some time it's produce an OSError. Line 17 in my code is this : print(qmc.readRaw())
          and the line 99 is this. register = self.i2c.readfrom_mem(QMC5883.ADDR, QMC5883.X_LSB, 9)
          I think there were an ; which is not needed.
          But something else is still not alright. I didn't find it yet.

          (6967, -4882, -7620, -1943)
          (4742, -7337, -7950, -1923)
          Traceback (most recent call last):
            File "main2.py", line 17, in <module>
            File "/flash/lib/qmc5883.py", line 99, in readRaw
          OSError: I2C bus error
          
          robert-hhR 1 Reply Last reply Reply Quote 0
          • T Offline
            tttadam @robert-hh
            last edited by

            @robert-hh

            This is the error what i get now. Maybe tomorrow with fresh eye I will see something :)

            [13, 64, 104, 119]
            11001001
            Traceback (most recent call last):
              File "main2.py", line 17, in <module>
              File "/flash/lib/qmc5883.py", line 95, in readRaw
              File "/flash/lib/qmc5883.py", line 81, in ready
            OSError: I2C bus error
            
            1 Reply Last reply Reply Quote 0
            • K Offline
              kuqizz
              last edited by

              This post is deleted!
              1 Reply Last reply Reply Quote 0
              • robert-hhR Offline
                robert-hh @tttadam
                last edited by

                @tttadam No, the ; is not needed (probably a left-over from an initial C-Code file), but it also does not hurt. There is another one in reconfig(). The OSError is probably caused by a I2C communication problem to the device. I did not let it run for longer than about 20 minutes or 1000 measurement. If you enclose that into a try/except clause, does the device recover?
                The temperature reading has an offset. That is also told in the data sheet. If I add 4900 to the value, it is almost OK. The value seems to be scaled as °C*100.

                T 1 Reply Last reply Reply Quote 0
                • T Offline
                  tttadam @robert-hh
                  last edited by

                  @robert-hh Okay, So I try-catch block for OSError for

                  status = self.i2c.readfrom_mem(QMC5883.ADDR, QMC5883.STATUS, 1)[0]
                  

                  and

                  return QMC5883.STATUS_DRDY
                  

                  lines. Looks like it's not recovering.

                  Something went wrongI2C bus error
                  Traceback (most recent call last):
                    File "main2.py", line 17, in <module>
                    File "/flash/lib/qmc5883.py", line 110, in readRaw
                    File "/flash/lib/qmc5883.py", line 97, in ready
                  NameError: local variable referenced before assignment.
                  

                  Did you used this library for porting?
                  Because it's mention something about readraw blocking.
                  Where did you find the desription about these four values? What are does mean?

                  The whole code looks like this, but it is the same, only thing I added, that I moved a i2c magic into the init method from main:

                  #
                  # ported from http://nobytes.blogspot.com/2018/03/qmc5883-magnetic-field-sensor.html
                  #
                  
                  import time
                  import struct
                  from machine import idle
                  
                  class QMC5883:
                      #Default I2C address
                      ADDR = 0x0D
                  
                      #QMC5883 = Register = numbers
                      X_LSB = 0
                      X_MSB = 1
                      Y_LSB = 2
                      Y_MSB = 3
                      Z_LSB = 4
                      Z_MSB = 5
                      STATUS = 6
                      T_LSB = 7
                      T_MSB = 8
                      CONFIG = 9
                      CONFIG2 = 10
                      RESET = 11
                      CHIP_ID = 13
                  
                      #Bit values for the STATUS register
                      STATUS_DRDY = 1
                      STATUS_OVL = 2
                      STATUS_DOR = 4
                  
                      #Oversampling values for the CONFIG register
                      MC5883L_CONFIG_OS512 = 0b00000000
                      CONFIG_OS256 = 0b01000000
                      CONFIG_OS128 = 0b10000000
                      CONFIG_OS64  = 0b11000000
                  
                      #Range values for the CONFIG register
                      CONFIG_2GAUSS = 0b00000000
                      CONFIG_8GAUSS = 0b00010000
                  
                      #Rate values for the CONFIG register
                      CONFIG_10HZ = 0b00000000
                      CONFIG_50HZ = 0b00000100
                      CONFIG_100HZ = 0b00001000
                      CONFIG_200HZ = 0b00001100
                  
                      #Mode values for the CONFIG register
                      CONFIG_STANDBY = 0b00000000
                      CONFIG_CONT = 0b00000001
                  
                  
                      def __init__(self, i2c):
                          self.i2c = i2c
                          self.i2c.writeto_mem(104, 107, 128) # reset MPU6050
                          self.i2c.writeto_mem(104, 107, 0) # disable sleep
                          self.i2c.writeto_mem(104, 55, 2)   # enable I2C passthru
                          self.oversampling = QMC5883.CONFIG_OS64
                          self.range = QMC5883.CONFIG_2GAUSS
                          self.rate = QMC5883.CONFIG_100HZ
                          self.mode = QMC5883.CONFIG_CONT
                          self.reset()
                  
                      def reconfig(self):
                          print("{0:b}".format(self.oversampling | self.range | self.rate | self.mode))
                          self.i2c.writeto_mem(QMC5883.ADDR, QMC5883.CONFIG, self.oversampling | self.range | self.rate | self.mode)
                  
                      def reset(self):
                          self.i2c.writeto_mem(QMC5883.ADDR, QMC5883.RESET, 0x01)
                          time.sleep(0.1)
                          self.reconfig()
                          time.sleep(0.01)
                  
                      def setOversampling(self, x):
                          self.oversampling = x
                          self.reconfig()
                  
                      def setRange(x):
                          self.range = x
                          self.reconfig()
                  
                      def setSamplingRate(self, x):
                          self.rate = x
                          self.reconfig()
                  
                      def ready(self):
                          try:
                              status = self.i2c.readfrom_mem(QMC5883.ADDR, QMC5883.STATUS, 1)[0]
                          except OSError as e:
                              print("Something went wrong"+str(e))
                              #TODO: Tune it a little   
                  
                          # prevent hanging up here.
                          # Happens when reading less bytes then then all 3 axis and will end up in a loop.
                          # So, return any data but avoid the loop.
                          if (status == QMC5883.STATUS_DOR):
                              print("fail")
                              try:
                                  return QMC5883.STATUS_DRDY
                              except OSError as e:
                                  print("Something went wrong"+str(e))
                                  #TODO: Tune it a little   
                  
                          return status & QMC5883.STATUS_DRDY
                  
                      def readRaw(self):
                          while not self.ready():
                              idle()
                              pass
                  
                          # Python performs a wrong casting at read_i2c_block_data.
                          # The filled buffer has to be onverted afterwards by mpdule Struct
                          register = self.i2c.readfrom_mem(QMC5883.ADDR, QMC5883.X_LSB, 9)
                  
                          # Convert the axis values to signed Short before returning
                          x, y, z, status, t = struct.unpack('<hhhBh', register)
                  
                          return (x, y, z, t)
                  
                  robert-hhR 1 Reply Last reply Reply Quote 0
                  • robert-hhR Offline
                    robert-hh @tttadam
                    last edited by

                    @tttadam
                    Thanks for the error log. But that only happens if you catch the error, print a message and then continue. The the symbol status is not defined, raising a new exception. So you would either add:
                    status = 0
                    or
                    return 0
                    after the print statements.

                    Did you used this library for porting?

                    No. I found Python code, which in turn might have been ported from that web side. See http://nobytes.blogspot.com/2018/03/qmc5883-magnetic-field-sensor.html with a link to another interesting port, of which I might carry over the calibration part.

                    I did intentionally not include that I2C magic, because that is only relevant for this GY87 board.

                    T 1 Reply Last reply Reply Quote 0
                    • T Offline
                      tttadam @robert-hh
                      last edited by

                      @robert-hh I found the issue.
                      GY-87 is on breadboard and and from the breadboard connected to the pytrack via dupont cables.
                      It was a contact issue. Sorry for the false alarm.
                      Can you help me out what are those 4 numbers mean, or where do I find a some info about that? My guess is that the last one is related to the temperature somehow but I am not sure.

                      (-925, -2192, -8705, -2071)
                      
                      robert-hhR 2 Replies Last reply Reply Quote 0
                      • robert-hhR Offline
                        robert-hh @tttadam
                        last edited by robert-hh

                        @tttadam The first three are the x,y,z values for the magnetic field, the last one is the temperature. All numbers a raw values. I have prepared a version which returns proper scaled gauss values for the magnetic field and °C for temperature. I did not test it yet, so I will send it later.
                        The temperature value has an offset. According to the data sheet, it is scaled properly, so differences are fine. But the offset for the absolute value has to be determined. For my device it is about 5000. So if you local temperature is about 30°C, then it's about the same. The scaling is 100LSB/°C. So if you divide the value by 100, you get °C (with offset).

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

                          @tttadam Updated code. The name of the methods are changed to reflect PEP8. SO
                          readRaw() -> read_raw
                          and a new
                          read_scaled(), which returns the values as Gauss and °C.
                          The constructor has an optional second argument for the temperature offset in °C .

                          #
                          # initial code by Sebastian Folz, M.Sc. at
                          # http://nobytes.blogspot.com/2018/03/qmc5883-magnetic-field-sensor.html
                          # which, I assume, was itself ported from C-Code
                          # See also https://github.com/RigacciOrg/py-qmc5883l
                          #
                          # Changes and Additions:
                          # - port to micropython's I2C methods
                          # - add method read() for scaled values
                          # - reading register values into a static buffer
                          # - parsed register values in one single struct call
                          # - changed method names according to PEP8
                          # - apply PyLint and fixed bugs & warnings reported by it
                          #
                          
                          import time
                          import struct
                          from machine import idle
                          
                          class QMC5883:
                              #Default I2C address
                              ADDR = 0x0D
                          
                              #QMC5883 Register numbers
                              X_LSB = 0
                              X_MSB = 1
                              Y_LSB = 2
                              Y_MSB = 3
                              Z_LSB = 4
                              Z_MSB = 5
                              STATUS = 6
                              T_LSB = 7
                              T_MSB = 8
                              CONFIG = 9
                              CONFIG2 = 10
                              RESET = 11
                              STATUS2 = 12
                              CHIP_ID = 13
                          
                              #Bit values for the STATUS register
                              STATUS_DRDY = 1
                              STATUS_OVL = 2
                              STATUS_DOR = 4
                          
                              #Oversampling values for the CONFIG register
                              CONFIG_OS512 = 0b00000000
                              CONFIG_OS256 = 0b01000000
                              CONFIG_OS128 = 0b10000000
                              CONFIG_OS64  = 0b11000000
                          
                              #Range values for the CONFIG register
                              CONFIG_2GAUSS = 0b00000000
                              CONFIG_8GAUSS = 0b00010000
                          
                              #Rate values for the CONFIG register
                              CONFIG_10HZ  = 0b00000000
                              CONFIG_50HZ  = 0b00000100
                              CONFIG_100HZ = 0b00001000
                              CONFIG_200HZ = 0b00001100
                          
                              #Mode values for the CONFIG register
                              CONFIG_STANDBY = 0b00000000
                              CONFIG_CONT = 0b00000001
                          
                              # Mode values for the CONFIG2 register
                              CONFIG2_INT_DISABLE = 0b00000001
                              CONFIG2_ROL_PTR = 0b01000000
                              CONFIG2_SOFT_RST = 0b10000000
                          
                          
                              def __init__(self, i2c, offset=50.0):
                                  self.i2c = i2c
                                  self.temp_offset = offset
                                  self.oversampling = QMC5883.CONFIG_OS64
                                  self.range = QMC5883.CONFIG_2GAUSS
                                  self.rate = QMC5883.CONFIG_100HZ
                                  self.mode = QMC5883.CONFIG_CONT
                                  self.register = bytearray(9)
                                  self.reset()
                          
                              def reset(self):
                                  self.i2c.writeto_mem(QMC5883.ADDR, QMC5883.RESET, 0x01)
                                  time.sleep(0.1)
                                  self.reconfig()
                          
                              def reconfig(self):
                                  # print("{0:b}".format(self.oversampling | self.range | self.rate | self.mode))
                                  self.i2c.writeto_mem(QMC5883.ADDR, QMC5883.CONFIG,
                                                       self.oversampling | self.range | self.rate | self.mode)
                                  time.sleep(0.01)
                                  self.i2c.writeto_mem(QMC5883.ADDR, QMC5883.CONFIG2, QMC5883.CONFIG2_INT_DISABLE)
                                  time.sleep(0.01)
                          
                              def set_oversampling(self, x):
                                  self.oversampling = x
                                  self.reconfig()
                          
                              def set_range(self, x):
                                  self.range = x
                                  self.reconfig()
                          
                              def set_sampling_rate(self, x):
                                  self.rate = x
                                  self.reconfig()
                          
                              def ready(self):
                                  status = self.i2c.readfrom_mem(QMC5883.ADDR, QMC5883.STATUS, 1)[0]
                                  # prevent hanging up here.
                                  # Happens when reading less bytes then then all 3 axis and will end up in a loop.
                                  # So, return any data but avoid the loop.
                                  if status == QMC5883.STATUS_DOR:
                                      print("Incomplete read")
                                      return QMC5883.STATUS_DRDY
                          
                                  return status & QMC5883.STATUS_DRDY
                          
                              def read_raw(self):
                                  try:
                                      while not self.ready():
                                          idle()
                                      self.i2c.readfrom_mem_into(QMC5883.ADDR, QMC5883.X_LSB, self.register)
                                  except OSError as e:
                                      print ("OSError", e)
                                      pass # just silently re-use the old values
                                  # Convert the axis values to signed Short before returning
                                  x, y, z, _, t = struct.unpack('<hhhBh', self.register)
                          
                                  return (x, y, z, t)
                          
                              def read_scaled(self):
                                  x, y, z, t = self.read_raw()
                                  scale = 12000 if self.range == QMC5883.CONFIG_2GAUSS else 3000
                          
                                  return (x / scale, y / scale , z / scale, (t / 100 + self.temp_offset))
                          
                          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
                          • 1 / 2
                          • First post
                            Last post

                          Pycom on Twitter

                          Pycom