From f3d9d80d24aa855ec1f47cc30c41fc6b6d5e1446 Mon Sep 17 00:00:00 2001 From: Robert HH Date: Sat, 16 Jun 2018 21:08:32 +0200 Subject: [PATCH 1/5] onewire.py: Optimize timing, enable CRC check and slim the code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reason for a change was the observation, that with about one in 300 measurements were wrong, some of them obvious. So I made a few changes: 1. The timing of readbit was changed to shorten the initial low puls to 2 µs by not calling sleep_us() at all and reading the state of the bit about 8 µs later. That matches better the datasheet. 2. The CRC check was enabled in the method read_temp_async(). The function returns None, when the CRC is wrong 3. The crc8() method is now table based, with two 16 byte tables. 4. Some inefficient optimizations in the onwire class were removed. Especially caching system call names in local variables for calls used only once in a functions was dropped. With change 1, readings with wrong crc are very rare now, and enabling the crc check allows to detect them. The crc8 implementation is a Python port of the code published here: http://lentz.com.au/blog/tag/crc-table-generator --- examples/DS18X20/onewire.py | 50 +++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/examples/DS18X20/onewire.py b/examples/DS18X20/onewire.py index ddf14df..f3c04a4 100644 --- a/examples/DS18X20/onewire.py +++ b/examples/DS18X20/onewire.py @@ -16,6 +16,13 @@ class OneWire: def __init__(self, pin): self.pin = pin self.pin.init(pin.OPEN_DRAIN, pin.PULL_UP) + self.disable_irq = machine.disable_irq + self.enable_irq = machine.enable_irq + self.crctab1 = (b"\x00\x5E\xBC\xE2\x61\x3F\xDD\x83" + b"\xC2\x9C\x7E\x20\xA3\xFD\x1F\x41") + self.crctab2 = (b"\x00\x9D\x23\xBE\x46\xDB\x65\xF8" + b"\x8C\x11\xAF\x32\xCA\x57\xE9\x74") + def reset(self): """ @@ -23,33 +30,30 @@ def reset(self): Returns True if a device asserted a presence pulse, False otherwise. """ sleep_us = time.sleep_us - disable_irq = machine.disable_irq - enable_irq = machine.enable_irq pin = self.pin pin(0) sleep_us(480) - i = disable_irq() + i = self.disable_irq() pin(1) sleep_us(60) status = not pin() - enable_irq(i) + self.enable_irq(i) sleep_us(420) return status def read_bit(self): sleep_us = time.sleep_us - enable_irq = machine.enable_irq pin = self.pin pin(1) # half of the devices don't match CRC without this line - i = machine.disable_irq() + i = self.disable_irq() pin(0) - sleep_us(1) + # skip sleep_us(1) here, results in a 2 us pulse. pin(1) - sleep_us(1) + sleep_us(5) # 8 us delay in total value = pin() - enable_irq(i) + self.enable_irq(i) sleep_us(40) return value @@ -69,14 +73,14 @@ def write_bit(self, value): sleep_us = time.sleep_us pin = self.pin - i = machine.disable_irq() + i = self.disable_irq() pin(0) sleep_us(1) pin(value) sleep_us(60) pin(1) sleep_us(1) - machine.enable_irq(i) + self.enable_irq(i) def write_byte(self, value): for i in range(8): @@ -97,19 +101,13 @@ def select_rom(self, rom): def crc8(self, data): """ - Compute CRC + Compute CRC, based on tables """ crc = 0 for i in range(len(data)): - byte = data[i] - for b in range(8): - fb_bit = (crc ^ byte) & 0x01 - if fb_bit == 0x01: - crc = crc ^ 0x18 - crc = (crc >> 1) & 0x7f - if fb_bit == 0x01: - crc = crc | 0x80 - byte = byte >> 1 + crc ^= data[i] ## just re-using crc as intermediate + crc = (self.crctab1[crc & 0x0f] ^ + self.crctab2[(crc >> 4) & 0x0f]) return crc def scan(self): @@ -181,7 +179,7 @@ def start_conversion(self, rom=None): """ if (rom==None) and (len(self.roms)>0): rom=self.roms[0] - if rom!=None: + if rom!=None: rom = rom or self.roms[0] ow = self.ow ow.reset() @@ -197,7 +195,7 @@ def read_temp_async(self, rom=None): return None if (rom==None) and (len(self.roms)>0): rom=self.roms[0] - if rom==None: + if rom==None: return None else: ow = self.ow @@ -205,7 +203,11 @@ def read_temp_async(self, rom=None): ow.select_rom(rom) ow.write_byte(0xbe) # Read scratch data = ow.read_bytes(9) - return self.convert_temp(rom[0], data) + crc = ow.crc8(data) + if crc == 0: + return self.convert_temp(rom[0], data) + else: + return None def convert_temp(self, rom0, data): """ From 86fa8e75916837200a608168a8340093691e8654 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 25 Jun 2018 18:49:12 +0200 Subject: [PATCH 2/5] Update onewire.py --- examples/DS18X20/onewire.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/DS18X20/onewire.py b/examples/DS18X20/onewire.py index f3c04a4..9a677b5 100644 --- a/examples/DS18X20/onewire.py +++ b/examples/DS18X20/onewire.py @@ -1,5 +1,9 @@ #!/usr/bin/env python3 - +# +# The crc8 implementation is a Python port of the C code published here: +# http://lentz.com.au/blog/tag/crc-table-generator +# As far as suitable, the copyrigth notice and the disclaimer of the link apply +# """ OneWire library for MicroPython """ From ed1999f81202c0cb08e16c8e2c8ece55b0cec100 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 25 Jun 2018 18:58:06 +0200 Subject: [PATCH 3/5] read_temp_asyn(): remove intermediate value --- examples/DS18X20/onewire.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/DS18X20/onewire.py b/examples/DS18X20/onewire.py index 9a677b5..e210640 100644 --- a/examples/DS18X20/onewire.py +++ b/examples/DS18X20/onewire.py @@ -207,8 +207,7 @@ def read_temp_async(self, rom=None): ow.select_rom(rom) ow.write_byte(0xbe) # Read scratch data = ow.read_bytes(9) - crc = ow.crc8(data) - if crc == 0: + if ow.crc8(data) == 0: return self.convert_temp(rom[0], data) else: return None From dabce8d9cc8cf3b9849446000d811a39c53b6093 Mon Sep 17 00:00:00 2001 From: Robert HH Date: Mon, 16 Jul 2018 20:51:14 +0200 Subject: [PATCH 4/5] onewire.py: simplified crc8 calculation --- examples/DS18X20/onewire.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/DS18X20/onewire.py b/examples/DS18X20/onewire.py index e210640..fd8026f 100644 --- a/examples/DS18X20/onewire.py +++ b/examples/DS18X20/onewire.py @@ -108,8 +108,8 @@ def crc8(self, data): Compute CRC, based on tables """ crc = 0 - for i in range(len(data)): - crc ^= data[i] ## just re-using crc as intermediate + for byte in data: + crc ^= byte ## just re-using crc as intermediate crc = (self.crctab1[crc & 0x0f] ^ self.crctab2[(crc >> 4) & 0x0f]) return crc From 1738aa29b2e1facc2f31cb94bddc8e7fecd86485 Mon Sep 17 00:00:00 2001 From: robert Date: Fri, 11 Oct 2019 17:23:27 +0200 Subject: [PATCH 5/5] lorawan-nano-gateway/nanogateway.py: Fix error handling With version 1.20.1, the object returned by: except Exception as exp: does not have the attribute exp.errno any more. Instead, exp.args[0] is used, which is backward compatible. --- examples/lorawan-nano-gateway/nanogateway.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/lorawan-nano-gateway/nanogateway.py b/examples/lorawan-nano-gateway/nanogateway.py index c1a26c5..dd14b95 100644 --- a/examples/lorawan-nano-gateway/nanogateway.py +++ b/examples/lorawan-nano-gateway/nanogateway.py @@ -390,7 +390,7 @@ def _udp_thread(self): except usocket.timeout: pass except OSError as ex: - if ex.errno != errno.EAGAIN: + if ex.args[0] != errno.EAGAIN: self._log('UDP recv OSError Exception: {}', ex) except Exception as ex: self._log('UDP recv Exception: {}', ex)