Temperature sensor: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
[[File:Green roofs 4 Dec 2021.png]] | [[File:Green roofs 4 Dec 2021.png]] | ||
= Authors = | |||
For more information contact Frederic Cherqui, Adam Simankowicz or Edmond Lascaris | |||
= Applications = | = Applications = | ||
Revision as of 05:08, 30 January 2023
Authors
For more information contact Frederic Cherqui, Adam Simankowicz or Edmond Lascaris
Applications
Temperature is one of the easiest sensor projects to start with. Applications include:
- Monitoring the temperature of a smart bee hive, rabbit hutch, chick coop, compost heap, worm farm, aquarium or wetland.
- Experimenting with concepts related to energy efficient building construction.
- Temperature compensation for other sensors (sensor values may change with changes in temperature).
- Making a weather station.
Key design considerations
- Circuit was originally powered with a 0.5W solar panel (0.5W Solar Panel 55x70 (Core Electronics) SKU: SS313070004). This was removed because the deep sleep function on the LoPy4 is very efficient. Removing the solar panel also keeps the circuit more simple.
- An external antenna (Pycom LoRa & Sigfox Antenna Kit (Core Electronics) SKU: CE04928) was originally used to improve signal strength. This was replaced with a more compact Molex flat antenna that could be mounted within the sensor housing.
- An electrolytic capacitor was added to the input of the Pololu voltage regulator to help prevent voltage spikes.
- Pull-up and pull-down resistors were added to the data lines on the temperature sensors and the voltage regulator.
- 5V regulated supplies are used by both temperature sensors, although these sensors can run on 3.3V.
- The 5V regulator supply is turned on and off using a shutdown pin (SHDN) and this also cuts the power supply to both temperature sensors.
- High precision 10k ohm resistors (<0.1% variation) were used for the voltage divider circuit.
- LoPy4 RGB LED used to signal program status. RGB LED can be commented out of program to reduce power consumption.
Types of temperature sensors
The are two common temperature sensors that are used in this sensor.
- An internal temperature and humidity sensor is the DHT22. This is included in all sensor project to monitor temperature and humidity conditions within the sensor housing. If the temperature gets too hot or too cold within the sensor housing this may shorten the life of the equipment and the batteries.
- An external temperatures sensor is the DS18B20. External sensors are typically water proof and have a longer cable. They can be used to monitor a range of interesting discovery learning projects.
DHT22 Internal temperature sensor
Overview
- The DHT22 temperature and humidity sensor that is placed within the sensor housing.
- Normally this sensor is used to detect abnormally high or low temperatures, or excess humidity.
- Extreme conditions can reduce the functional life of the sensor.
- The following link is a good reference to the use of the sensor. [[1]]
Important notes
- DHT22 Pin layout
- Pin 1 is VCC (Power Supply)
- Pin 2 is DATA (The data signal)
- Pin 3 is NULL (Do not connect)
- Pin 4 is GND (Ground)
- The DHT22 is a slow sensor which requires up to 2 seconds to take a reading
- It can operate at 3.3V or 5V, but 5V is preferable.
- The DHT22 requires a 10K pull-up resistor between pins 1 (VCC) and 2 (DATA). Failure to do this will result in data loss.
DS18B20 External temperature sensor
- The DS18B20 temperature sensor is waterproof and can be used for most external temperature sensing functions.
- It uses the one-wire library for communication.
- It can be powered using 3.3V or 5V supplies.
- For correct operation it requires a 10k ohm pull-up resistor connected between VCC and DATA pins.
Software installation requirements
- Atom installed on PC or Mac with Pymakr package installed
Parts list
| Item description | Number | Supplier | Part number | URL | Cost | Notes |
|---|---|---|---|---|---|---|
| Solder-able Breadboard | 1 | Core Electronics | SKU: PRT-12070 | https://core-electronics.com.au/solder-able-breadboard.html | $8.20 | 55mm x 80mm |
| Pycom LoPy4 (LoRaWAN) | 1 | Core Electronics | SKU: CE05399 | https://core-electronics.com.au/pycom-lopy4.html | $68.95 | Microcontroller with LoRa |
| Pycom Expansion Board 3.1 | 1 | Core Electronics | SKU: CE05545 | https://core-electronics.com.au/pycom-expansion-board-3-1.html | $35.95 | Used to program LoPy4 |
| DHT22 Temperature and Relative Humidity Sensor Module | 1 | Core Electronics | SKU: 018-DHT22 | https://core-electronics.com.au/dht22-temperature-and-relative-humidity-sensor-module.html | $13.75 | Temp precision +/-0.5degC |
| Temperature Sensor - Waterproof (DS18B20) | 1 | Core Electronics | SKU: SEN-11050 | https://core-electronics.com.au/temperature-sensor-waterproof-ds18b20.html | $18.80 | 1-Wire interface library |
| Electrolytic Decoupling Capacitors - 100uF/25V | 1 | Core Electronics | SKU: COM-00096 | https://core-electronics.com.au/electrolytic-decoupling-capacitors-100uf-25v.html | $0.29 | Used to block high voltage spikes |
| Pololu 5V Step-Up Voltage Regulator U1V11F5 | 1 | Core Electronics | SKU: POLOLU-2562 | https://core-electronics.com.au/pololu-5v-step-up-voltage-regulator-u1v11f5.html | $11.65 | 5V supply for sensors |
| Resistor 10K Ohm 1/4 Watt PTH - 20 pack (Thick Leads) | 1 | Core Electronics | SKU: PRT-14491 | https://core-electronics.com.au/resistor-10k-ohm-1-4-watt-pth-20-pack-thick-leads.html | $2.05 | Pack of 20 resistors |
| Hook-Up Wire - Assortment (Solid Core, 22 AWG) | 1 | Core Electronics | SKU: PRT-11367 | https://core-electronics.com.au/hook-up-wire-assortment-solid-core-22-awg.html | $40.95 | Solid core for wiring circuit |
| 36-pin 0.1 Female header - pack of 5! | 1 | Core Electronics | SKU: ADA598 | https://core-electronics.com.au/20-pin-0-1-female-header-pack-of-5.html | $6.30 | Used for plugin components |
| 10 Pcs 40 Pin Headers - Straight | 1 | Core Electronics | SKU: FIT0084 | https://core-electronics.com.au/10-pcs-40-pin-headers-straight.html | $4.70 | Used to make sensors plugin |
| Holman 100mm PVC DWV Push On Cap | 1 | Bunnings | DWVF0198 | https://www.bunnings.com.au/holman-100mm-pvc-dwv-push-on-cap_p4770360 | $3.30 | End caps for sensor housing |
| Holman 100mm x 3m PVC DWV Pipe | 1 | Bunnings | DWV1003 | https://www.bunnings.com.au/holman-100mm-x-3m-pvc-dwv-pipe_p4770345 | $33.00 | Cut in store for easier transport |
| Selleys 310g Clear Glass Silicone Sealant | 1 | Bunnings | Model no 9300697100382 | https://www.bunnings.com.au/selleys-310g-clear-glass-silicone-sealant_p1234951 | $15.00 | Used with caulking gun |
| 18650 2600mAh Li-ion Protected Battery | 1 | Jaycar | CAT.NO: SB2299 | https://www.jaycar.com.au/18650-2600mah-li-ion-protected-battery/p/SB2299 | $21.95 | Inbuilt safety protection |
| Single 18650 Battery Holder | 1 | Jaycar | CAT.NO: PH9205 | https://www.jaycar.com.au/single-18650-battery-holder/p/PH9205 | $3.25 | Battery holder |
| Dual-Channel Li-ion / Ni-MH Battery Charger | 1 | Jaycar | CAT.NO: MB3635 | https://www.jaycar.com.au/dual-channel-li-ion-ni-mh-battery-charger/p/MB3635 | $39.95 | Only one required for all projects |
| MOLEX 211140-0100.. 868/915MHZ FLEXIBLE ANTENNA100MM CABLE | 1 | Element-14 | Order code 3225220 | https://au.element14.com/molex/211140-0100/868-915mhz-flexible-antenna100mm/dp/3225220?ost=molex++211140-0100..++868%2F915mhz+flexible+antenna100mm+cable | $2.84 | Attached to LoPy4 |
| Through Hole Resistor, 10 kohm, CMF, 500 mW, ± 0.1%, Axial Leaded, 250 V | 2 | Element-14 | Order code 3596907 | https://au.element14.com/vishay/cmf5510k000beea/res-10k-0-5w-axial-metal-film/dp/3596907?ost=vishay++cmf5510k000beea | $1.56 | High precision resistor for voltage divider |
| P9406 • 6 Pin 5A Screw-On Female Line IP66 Waterproof Socket | 1 | Altronics | Code: P9406 | https://www.altronics.com.au/p/p9406-amphenol-ltw-6-pin-5a-screw-on-female-line-ip66-waterproof-socket/ | $23.50 | Connected to sensor housing |
| P9416 • 6 Pin 5A Screw-On Male Chassis IP67 Waterproof Plug | 1 | Altronics | Code P9416 | https://www.altronics.com.au/p/p9416-amphenol-ltw-6-pin-5a-screw-on-male-chassis-ip67-waterproof-plug/ | $17.25 | Connected to sensor |
Circuit schematic
Photos of completed circuit board
- Aerial view of board.
- Close up of board.
- Close up of board without LoPy4.
- Close up of board from DHT22 temperature sensor end.
- Close up aerial view of board without LoPy4.
Electronic circuit construction
Pycom LoPy4
- LED on LoPy4 nearest top end of board.
- Cut female header pins to 14 pin length (x2)
- Solder from pins C10 to C23 and from pins H10 to H23
- VIN (3.5-4.2V direct from battery rail) from positive rail nearest to J to pin I10
- GND from negative rail nearest to J to pin J11
Voltage divider circuit
- 10k ohm high precision resistors x 2
- I16 to positive rail nearest to J
- J16 to negative rail nearest to J
Power supply
- Use either 3 x 1.2V Panasonic eneloop Ni-MH batteries
- Or use 1 x 3.7V Li-Ion battery
- Attach positive and negative rails nearest to J
DHT22 temperature and humidity sensor
- C30 to Pin 1 VCC (Power Supply)
- C29 to Pin 2 DATA (The data signal)
- C28 to Pin 3 NULL (Do not connect)
- C27 to Pin 4 GND (Ground)
- 10k ohm resistor (low precision) D30 to E29
- GND connection from E27 to negative rail nearest to J
- DATA connection from D29 to E21
- VCC connection from E30 to H4
Pololu 5V Step-Up Voltage Regulator U1V11F5
- Cut female header pins to 4 pin length
- Solder from pins F1 to F4
- Solder right angle male header pins to Regulator so board stands upright (supplied with regulator)
- F1 to shutdown (SHDN)
- F2 to input voltage (VIN) - from battery
- F3 to ground (GND)
- F4 to output voltage (VOUT) - 5V regulated
- 10k ohm pull-down resistor (low precision) J1 to negative rail nearest to J
- GND connection from J3 to negative rail nearest to J
- Electrolytic Decoupling Capacitor positive terminal G2
- Electrolytic Decoupling Capacitor negative terminal G3
- VIN (3.3V from LoPy4) from J12 to J2
- SHDN (shutdown) pin connected from pin E20 to H1
DS18B20 external temperature sensor
- Cut female header pins to 3 pin length
- Solder from pins A1 to A3
- A1 to DATA
- A2 to VCC
- A3 to GND
- Solder right angle male header pins to temperature sensor leads
- GND connection from pin B3 to B27
- DATA connection from pin D1 to D23
- VCC (5V regulated supply) connection from pin E2 to I4
- 10k ohm pull-up resistor (low precision) from pin B1 to B2
MicroPython code for Pycom LoPy4 microcontroller
- There are several different code segments used on the Pycom LoPy4 microcontroller to monitor temperature.
- boot.py - bootstrap program that runs first.
- main.py - main program where program code is contained
- pymakr.conf - configuration file for Atom and Pymakr package
- lib - directory that contains additional library files
- config.py - list of static variables
- dth.py - library for DHT22 internal temperature sensor
- lora.py - library for LoRa. Contains private keys
- onewire.py - onewire library to communicate with DS18B20 external temperature sensor
boot.py
# boot.py -- run on boot-up
import pycom
from machine import UART
import machine
import os
from network import Bluetooth
from network import WLAN
if pycom.heartbeat() == True:
pycom.heartbeat(False)
if pycom.wifi_on_boot() == True:
pycom.wifi_on_boot(False)
#wlan = WLAN()
#wlan.deinit()
bluetooth = Bluetooth()
bluetooth.deinit()
uart = UART(0, baudrate=115200)
os.dupterm(uart)
machine.main('main.py')
main.py
# main.py
import binascii #module that makes conversion between binary and Ascii
import config
from dth import DTH
import gc # garbage collection
from lora import LoraAU915
import machine
from machine import Pin
from machine import ADC
from onewire import DS18X20
from onewire import OneWire
import pycom
import network
import socket
import time
import ustruct
import utime
#INIT EVERYTHING
sleep_duration = 600 # in seconds (10 minutes)
pycom.rgbled(config.GREEN)
# power up sensors
sensor_power = Pin(config.POWER_PIN, mode=Pin.OUT) # power for all sensors
sensor_power.value(1) # power up
time.sleep(2) # wait 2 seconds to stabilise
print("starting main")
pycom.rgbled(config.OFF)
# voltage divider setup to monitor battery voltage
adc = ADC()
time.sleep(2) # wait 2 seconds to stabilise
# garbage collection enabled
gc.enable()
# config LoRa communications
s = LoraAU915.setupLora()
####################################
### LOOP IF NO NEED TO SLEEP #######
####################################
while True:
sensor_power.value(1) # power up
time.sleep(1)
pycom.rgbled(config.ORANGE)
time.sleep(1)
pycom.rgbled(config.OFF)
# TEMPERATURE AND HUMIDITY OF AMBIENT AIR
j=0
th = DTH(config.DHT22_DATA,1)
time.sleep(1)
while j < 5:
result = th.read()
if result.temperature != 0:
print("temperature is not zero - breaking from while loop")
break
time.sleep(1)
j += 1
print("Error code %d" % result.error_code)
print(j)
print("Temperature = = %.2f C" % result.temperature)
print("Humidity = %.2f %%" % result.humidity)
print("Error code final %d" % result.error_code)
# BATTERY
meanbatt=0
j=0
while j < 5:
utime.sleep(0.2)
batt = adc.channel(attn=3, pin=config.VOLTMETER)
meanbatt += batt.voltage()/1000
j += 1
batt_volt = (meanbatt/j)*config.BATCOEFF #multiplcator coef to adjust the real value of the battery voltage
print("Battery voltage = %.2f V" % batt_volt)
#DS18B20 TEMPERATURE SENSOR
#https://docs.pycom.io/tutorials/hardware/owd/#app
#Also note - needed to add a 10k resistor pull up on the board
#Internal pull up resistor was not sufficient
#used this reference for Raspberry Pi circuit which includes 5k to 10k pull up resistor
#https://www.circuitbasics.com/raspberry-pi-ds18b20-temperature-sensor-tutorial/
ow = OneWire(Pin(config.DS18B20_DATA))
temp = DS18X20(ow)
soil_temp = temp.start_conversion()
time.sleep(1)
soil_temp = temp.read_temp_async()
time.sleep(1)
if soil_temp != None:
print("Soil temperature = %.2f C" % soil_temp)
else:
print("No data received from soil temp probe")
pycom.rgbled(config.RED)
time.sleep(1)
pycom.rgbled(config.OFF)
soil_temp = 0.0
soil_temp_t = int(soil_temp*100)
print(soil_temp_t)
batt_volt_t = int(batt_volt*100)
print(batt_volt_t)
sens_temp = int(result.temperature*100)
print(sens_temp)
sens_humi = int(result.humidity*100)
print(sens_humi)
# converting 2 byte values into single bytes for transmission
soil1 = int(soil_temp_t//256)
soil2 = int(soil_temp_t%256)
batt1 = int(batt_volt_t//256)
batt2 = int(batt_volt_t%256)
temp1 = int(sens_temp//256)
temp2 = int(sens_temp%256)
humi1 = int(sens_humi//256)
humi2 = int(sens_humi%256)
print("sending to TTN")
s.setblocking(True)
print(soil1,soil2,batt1,batt2,temp1,temp2,humi1,humi2)
s.send(bytes([soil1,soil2,batt1,batt2,temp1,temp2,humi1,humi2]))
s.setblocking(False)
# Save LoRa settings before deep sleep
LoraAU915.saveLora()
print("going to sleep")
sensor_power.value(0) # power down
# note that batt_volt has been multiplied by 100 for transmission
if (batt_volt > 4.0):
# to minimise writes to onboard memory and for teaching
sleep_duration = 600 # sleep for 10 minutes
print("Time delay sleep only")
time.sleep(sleep_duration) # sleep in seconds
else:
sleep_duration = 3600 # sleep for 60 minutes
print("Sleep using deepsleep")
machine.deepsleep(1000 * sleep_duration) # deepsleep in ms
pymakr.conf
{
"address": "COM10",
"username": "micro",
"password": "python",
"sync_folder": "",
"sync_file_types": [
"py",
"txt",
"log",
"json",
"xml",
"html",
"js",
"css",
"mpy",
"pem",
"cet",
"crt",
"key"
],
"sync_all_file_types": false,
"open_on_start": true,
"safe_boot_on_upload": false,
"py_ignore": [],
"fast_upload": false
}
config.py
########################
# config.py
########################
# PINs
POWER_PIN = 'P9' # pin that powers enable pin on 5V Pololu regulator
DHT22_DATA = 'P10' # pin that receives data from internal temp/humidity sensor
DS18B20_DATA = 'P12' # pin that receives data from external temp sensor
VOLTMETER='P20' # pin connected to voltage divider
BATCOEFF = 2.0 # to calculate the real voltage
# led colors
OFF = 0x000000
WHITE=0xFFFFCC
RED = 0xff0000
BLUE = 0x0000ff
GREEN = 0x00ff00
YELLOW = 0x7f7f00
PURPLE = 0x7f007f
ORANGE = 0xFF9900
dth.py
# dth.py
import time
import pycom
from machine import enable_irq, disable_irq, Pin
# DHT22 sensor pins - when sensor lying down
#1 - VCC - red wire Connect to 3.3 - 5V power. Sometime 3.3V power isn't enough in which case try 5V power.
#2 - Data out - white or yellow wire
#3 - Not connected
#4 - Ground - black wire
# Note - 10 Kohm resistor between VCC and the data pin
# current draw 1.5mA - pycom advise up to 6mA per pin
class DTHResult:
'DHT sensor result returned by DHT.read() method'
ERR_NO_ERROR = 0
ERR_MISSING_DATA = 1
ERR_CRC = 2
error_code = ERR_NO_ERROR
temperature = -1
humidity = -1
def __init__(self, error_code, temperature, humidity):
self.error_code = error_code
self.temperature = temperature
self.humidity = humidity
def is_valid(self):
return self.error_code == DTHResult.ERR_NO_ERROR
class DTH:
'DHT sensor (dht11, dht21,dht22) reader class for Pycom'
#__pin = Pin('P3', mode=Pin.OPEN_DRAIN)
__dhttype = 0
def __init__(self, pin, sensor=0):
self.__pin = Pin(pin, mode=Pin.OPEN_DRAIN)
self.__dhttype = sensor
self.__pin(1)
time.sleep(1.0)
def read(self):
# pull down to low
self.__send_and_sleep(0, 0.019)
data = pycom.pulses_get(self.__pin,100)
self.__pin.init(Pin.OPEN_DRAIN)
self.__pin(1)
#print(data)
bits = []
for a,b in data:
if a ==1 and 18 <= b <= 28:
bits.append(0)
if a ==1 and 65 <= b <= 75:
bits.append(1)
#print("longueur bits : %d " % len(bits))
if len(bits) != 40:
return DTHResult(DTHResult.ERR_MISSING_DATA, 0, 0)
#print(bits)
# we have the bits, calculate bytes
the_bytes = self.__bits_to_bytes(bits)
# calculate checksum and check
checksum = self.__calculate_checksum(the_bytes)
if the_bytes[4] != checksum:
return DTHResult(DTHResult.ERR_CRC, 0, 0)
# ok, we have valid data, return it
[int_rh, dec_rh, int_t, dec_t, csum] = the_bytes
if self.__dhttype==0: #dht11
rh = int_rh #dht11 20% ~ 90%
t = int_t #dht11 0..50°C
else: #dht21,dht22
rh = ((int_rh * 256) + dec_rh)/10
t = (((int_t & 0x7F) * 256) + dec_t)/10
if (int_t & 0x80) > 0:
t *= -1
return DTHResult(DTHResult.ERR_NO_ERROR, t, rh)
def __send_and_sleep(self, output, mysleep):
self.__pin(output)
time.sleep(mysleep)
def __bits_to_bytes(self, bits):
the_bytes = []
byte = 0
for i in range(0, len(bits)):
byte = byte << 1
if (bits[i]):
byte = byte | 1
else:
byte = byte | 0
if ((i + 1) % 8 == 0):
the_bytes.append(byte)
byte = 0
#print(the_bytes)
return the_bytes
def __calculate_checksum(self, the_bytes):
return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255
lora.py
# lora.py
import config
import utime
import time
from network import LoRa
import socket
import binascii
import struct
import machine
class LoraAU915: #Australia AU915
# https://stackoverflow.com/questions/5690888/variable-scopes-in-python-classes
lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AU915)
def setupLora():
#lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AU915)
# Initialise LoRa in LORAWAN mode.
# create an OTAA authentication parameters, change them to the provided credentials
# whi-seedling-1
app_eui = binascii.unhexlify('YOUR_DEVICE_APP_EUI')
app_key = binascii.unhexlify('YOUR_DEVICE_APP_KEY')
#Limit channels for AU915
for i in range(0,8):
LoraAU915.lora.remove_channel(i)
for i in range(16,65):
LoraAU915.lora.remove_channel(i)
for i in range(66,72):
LoraAU915.lora.remove_channel(i)
# join a network using OTAA (Over the Air Activation)
#uncomment below to use LoRaWAN application provided dev_eui
#https://docs.pycom.io/tutorials/networks/lora/nvram/
new_connection = True
LoraAU915.lora.nvram_restore()
if(LoraAU915.lora.has_joined() == False):
print("LoRa memory restored but need to join LoRa")
LoraAU915.lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=0)
else:
print("Memory restored and have rejoined LoRa with previous Dev Add")
new_connection = False
# wait until the module has joined the network
while not LoraAU915.lora.has_joined():
utime.sleep(1)
if utime.time() > 15:
print("possible timeout")
machine.reset()
pass
print('Joined')
# create a LoRa socket
s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
if new_connection:
# set the LoRaWAN data rate
s.setsockopt(socket.SOL_LORA, socket.SO_DR, 0) # dr0 equiv to SF12
# make the socket non-blocking
s.setblocking(False)
print("LoRa connection set up")
utime.sleep_ms(5)
return s
def saveLora():
LoraAU915.lora.nvram_save()
print("LoRa setting saved to memory")
onewire.py
"""
OneWire library for MicroPython
"""
import time
import machine
class OneWire:
CMD_SEARCHROM = const(0xf0)
CMD_READROM = const(0x33)
CMD_MATCHROM = const(0x55)
CMD_SKIPROM = const(0xcc)
def __init__(self, pin):
self.pin = pin
self.pin.init(pin.OPEN_DRAIN, pin.PULL_UP)
def reset(self):
"""
Perform the onewire reset function.
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()
pin(1)
sleep_us(60)
status = not pin()
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()
pin(0)
sleep_us(1)
pin(1)
sleep_us(1)
value = pin()
enable_irq(i)
sleep_us(40)
return value
def read_byte(self):
value = 0
for i in range(8):
value |= self.read_bit() << i
return value
def read_bytes(self, count):
buf = bytearray(count)
for i in range(count):
buf[i] = self.read_byte()
return buf
def write_bit(self, value):
sleep_us = time.sleep_us
pin = self.pin
i = machine.disable_irq()
pin(0)
sleep_us(1)
pin(value)
sleep_us(60)
pin(1)
sleep_us(1)
machine.enable_irq(i)
def write_byte(self, value):
for i in range(8):
self.write_bit(value & 1)
value >>= 1
def write_bytes(self, buf):
for b in buf:
self.write_byte(b)
def select_rom(self, rom):
"""
Select a specific device to talk to. Pass in rom as a bytearray (8 bytes).
"""
self.reset()
self.write_byte(CMD_MATCHROM)
self.write_bytes(rom)
def crc8(self, data):
"""
Compute CRC
"""
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
return crc
def scan(self):
"""
Return a list of ROMs for all attached devices.
Each ROM is returned as a bytes object of 8 bytes.
"""
devices = []
diff = 65
rom = False
for i in range(0xff):
rom, diff = self._search_rom(rom, diff)
if rom:
devices += [rom]
if diff == 0:
break
return devices
def _search_rom(self, l_rom, diff):
if not self.reset():
return None, 0
self.write_byte(CMD_SEARCHROM)
if not l_rom:
l_rom = bytearray(8)
rom = bytearray(8)
next_diff = 0
i = 64
for byte in range(8):
r_b = 0
for bit in range(8):
b = self.read_bit()
if self.read_bit():
if b: # there are no devices or there is an error on the bus
return None, 0
else:
if not b: # collision, two devices with different bit meaning
if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i):
b = 1
next_diff = i
self.write_bit(b)
if b:
r_b |= 1 << bit
i -= 1
rom[byte] = r_b
return rom, next_diff
class DS18X20(object):
def __init__(self, onewire):
self.ow = onewire
self.roms = [rom for rom in self.ow.scan() if rom[0] == 0x10 or rom[0] == 0x28]
self.fp = True
try:
1/1
except TypeError:
self.fp = False # floatingpoint not supported
def isbusy(self):
"""
Checks wether one of the DS18x20 devices on the bus is busy
performing a temperature convertion
"""
return not self.ow.read_bit()
def start_conversion(self, rom=None):
"""
Start the temp conversion on one DS18x20 device.
Pass the 8-byte bytes object with the ROM of the specific device you want to read.
If only one DS18x20 device is attached to the bus you may omit the rom parameter.
"""
if (rom==None) and (len(self.roms)>0):
rom=self.roms[0]
if rom!=None:
rom = rom or self.roms[0]
ow = self.ow
ow.reset()
ow.select_rom(rom)
ow.write_byte(0x44) # Convert Temp
def read_temp_async(self, rom=None):
"""
Read the temperature of one DS18x20 device if the convertion is complete,
otherwise return None.
"""
if self.isbusy():
return None
if (rom==None) and (len(self.roms)>0):
rom=self.roms[0]
if rom==None:
return None
else:
ow = self.ow
ow.reset()
ow.select_rom(rom)
ow.write_byte(0xbe) # Read scratch
data = ow.read_bytes(9)
return self.convert_temp(rom[0], data)
def convert_temp(self, rom0, data):
"""
Convert the raw temperature data into degrees celsius and return as a fixed point with 2 decimal places.
"""
temp_lsb = data[0]
temp_msb = data[1]
if rom0 == 0x10:
if temp_msb != 0:
# convert negative number
temp_read = temp_lsb >> 1 | 0x80 # truncate bit 0 by shifting, fill high bit with 1.
temp_read = -((~temp_read + 1) & 0xff) # now convert from two's complement
else:
temp_read = temp_lsb >> 1 # truncate bit 0 by shifting
count_remain = data[6]
count_per_c = data[7]
if self.fp:
return temp_read - 25 + (count_per_c - count_remain) / count_per_c
else:
return 100 * temp_read - 25 + (count_per_c - count_remain) // count_per_c
elif rom0 == 0x28:
temp = None
if self.fp:
temp = (temp_msb << 8 | temp_lsb) / 16
else:
temp = (temp_msb << 8 | temp_lsb) * 100 // 16
if (temp_msb & 0xf8) == 0xf8: # for negative temperature
temp -= 0x1000
return temp
else:
assert False
Trouble shooting
- Ensure that all sensors have pull-up resistors connected to data lines.
- Ensure that the Pololu 5V regulator has a pull-down resistor connected to the shutdown (SHDN) pin.
- Ensure that the Pololu 5V regulator is plugged in correctly.
- Ensure that batteries used to power the circuit are fully charged and are in good health. The LoPy will not function correctly if batteries cannot deliver sufficient current on start up.
- Ensure that the Electrolytic capacitor is installed with the correct polarity.
- Ensure that the Pycom LoPy4 is plugged in correctly (LED closest to 5V regulator)





