MicroPython Skripte der unterschiedlichen Sensoren

Phyphox-Bibliothek

Zuerst muss man den PhyphoxBLE-Ordner, in der die Phyphox-Bibliothek gespeichert ist, herunterladen: https://github.com/phyphox/phyphox-micropython/tree/main/phyphoxBLE

Diesen Ordner muss man dann auf den ESP laden

Um Sensoren auszulesen und diese in Phyphox anzeigen zu lassen muss man nun die Bibliothek des gewünschten Sensors herunter- und auf den Mikrocontroller daraufladen

Kraftsensor

Kraftsensor-Bibliothek

from machine import Pin, enable_irq, disable_irq, idle

class HX711:
    def __init__(self, dout, pd_sck, gain=128):

        self.pSCK = Pin(pd_sck , mode=Pin.OUT)
        self.pOUT = Pin(dout, mode=Pin.IN, pull=Pin.PULL_DOWN)
        self.pSCK.value(False)

        self.GAIN = 0
        self.OFFSET = 0
        self.SCALE = 1
        
        self.time_constant = 0.1
        self.filtered = 0

        self.set_gain(gain);

    def set_gain(self, gain):
        if gain is 128:
            self.GAIN = 1
        elif gain is 64:
            self.GAIN = 3
        elif gain is 32:
            self.GAIN = 2

        self.read()
        self.filtered = self.read()
        print('Gain & initial value set')
    
    def is_ready(self):
        return self.pOUT() == 0

    def read(self):
        # wait for the device being ready
        while self.pOUT() == 1:
            idle()

        # shift in data, and gain & channel info
        result = 0
        for j in range(24 + self.GAIN):
            state = disable_irq()
            self.pSCK(True)
            self.pSCK(False)
            enable_irq(state)
            result = (result << 1) | self.pOUT()

        # shift back the extra bits
        result >>= self.GAIN

        # check sign
        if result > 0x7fffff:
            resualt -= 0x1000000

        return result

    def read_average(self, times=3):
        sum = 0
        for i in range(times):
            sum += self.read()
        return sum / times

    def read_lowpass(self):
        self.filtered += self.time_constant * (self.read() - self.filtered)
        return self.filtered

    def get_value(self, times=3):
        return self.read_average(times) - self.OFFSET

    def get_units(self, times=3):
        return self.get_value(times) / self.SCALE

    def tare(self, times=15):
        sum = self.read_average(times)
        self.set_offset(sum)

    def set_scale(self, scale):
        self.SCALE = scale

    def set_offset(self, offset):
        self.OFFSET = offset

    def set_time_constant(self, time_constant = None):
        if time_constant is None:
            return self.time_constant
        elif 0 < time_constant < 1.0:
            self.time_constant = time_constant

    def power_down(self):
        self.pSCK.value(False)
        self.pSCK.value(True)

    def power_up(self):
        self.pSCK.value(False)

Code für den Kraftsensor

from phyphoxBLE import PhyphoxBLE, Experiment
from machine import Pin, I2C, ADC
import machine
import time
from hx711 import HX711

dout_pin = 22
sck_pin = 23

hx = HX711(dout_pin, sck_pin)
hx.set_scale(1024)
hx.tare()

lastTimestamp = 0
blinkInterval = 1000
p = PhyphoxBLE()
p.debug = False

def main():
global lastTimestamp
p.start("Kraftsensor")
p._write_callback = receivedData
plot = Experiment()
plot.setTitle("Gewichts-Messung")
plot.setCategory("Micropython Experiments")
plot.setDescription("Der Microcontroller misst das Gewicht der Last")

firstView = Experiment.View()
firstView.setLabel("Gewichts-Messung")
secondView = Experiment.View()
secondView.setLabel("Einstellungen")

abstand = Experiment.Separator()
abstand.setHeight(2.0)

kraftgraph = Experiment.Graph()
kraftgraph.setLabel("Gewichtsmessung")
kraftgraph.setUnitX("s")
kraftgraph.setUnitY("g")
kraftgraph.setLabelX("Zeit")
kraftgraph.setLabelY("Gewicht")
kraftgraph.setXPrecision(1)
kraftgraph.setYPrecision(2)
kraftgraph.setChannel(0,1)

myValue1 = Experiment.Value()
myValue1.setLabel("Gewicht")
myValue1.setPrecision(2)
myValue1.setUnit("g")
myValue1.setColor("FFFFFF")
myValue1.setChannel(1)
myValue1.setXMLAttribute("size=\"2\"")

mySet = Experiment.ExportSet()
mySet.setLabel("Gewichts-Messungen")

myData1 = Experiment.ExportData() 
myData1.setLabel("Zeit")
myData1.setDatachannel(0)

myData2 = Experiment.ExportData() 
myData2.setLabel("Gewicht g")
myData2.setDatachannel(1)

Interval = Experiment.Edit() 
Interval.setLabel("Gewünschter Interval:")
Interval.setUnit("s")
Interval.setSigned(False)
Interval.setDecimal(False)
Interval.setChannel(1)
Interval.setXMLAttribute("min=\"1\"")

myValue2 = Experiment.Value()
myValue2.setLabel("Aktueller Interval:")
myValue2.setPrecision(0)
myValue2.setUnit("s")
myValue2.setColor("FFFFFF")
myValue2.setChannel(2)
myValue2.setXMLAttribute("size=\"2\"")

firstView.addElement(abstand)
firstView.addElement(kraftgraph)
firstView.addElement(abstand)
firstView.addElement(myValue1)
secondView.addElement(abstand)
secondView.addElement(myValue2)
secondView.addElement(Interval)
plot.addView(firstView)
plot.addView(secondView)
mySet.addElement(myData1)
mySet.addElement(myData2)
plot.addExportSet(mySet)
p.addExperiment(plot)

while True:
    if time.ticks_ms()-lastTimestamp > blinkInterval:
        lastTimestamp = time.ticks_ms()
        try:
            weight = hx.get_units()
        except ValueError:
            weight=0
        print(weight)
        machine.idle()
        p.write(weight,blinkInterval/1000)

def receivedData():
global blinkInterval
receivedInterval = p.read()
if receivedInterval > 0 and receivedInterval != blinkInterval:
print("New Interval: ", receivedInterval)
blinkInterval = receivedInterval*1000

if name == "main":
main()

Temperatursensor

Bibliothek vom Temperatursensor

OneWire

# DS18x20 temperature sensor driver for MicroPython.
# MIT license; Copyright (c) 2016 Damien P. George

from micropython import const
from machine import Pin

CMD_CONVERT = const(0x44)
CMD_RDSCRATCH = const(0xbe)
CMD_WRSCRATCH = const(0x4e)
CMD_RDPOWER = const(0xb4)
PULLUP_ON = const(1)
PULLUP_OFF = const(0)

class DS18X20:
    def __init__(self, onewire):
        self.ow = onewire
        self.buf = bytearray(9)
        self.config = bytearray(3)
        self.power = 1 # strong power supply by default
        self.powerpin = None

    def powermode(self, powerpin=None):
        if self.powerpin is not None: # deassert strong pull-up
            self.powerpin(PULLUP_OFF)
        self.ow.writebyte_byte(self.ow.CMD_SKIPROM)
        self.ow.writebyte(CMD_RDPOWER)
        self.power = self.ow.readbit()
        if powerpin is not None:
            assert type(powerpin) is Pin, "Parameter must be a Pin object"
            self.powerpin = powerpin
            self.powerpin.init(mode=Pin.OUT, value=0)
        return self.power

    def scan(self):
        if self.powerpin is not None: # deassert strong pull-up
            self.powerpin(PULLUP_OFF)
        return [rom for rom in self.ow.scan() if rom[0] in (0x10, 0x22, 0x28)]

    def convert_temp(self, rom=None):
        if self.powerpin is not None: # deassert strong pull-up
            self.powerpin(PULLUP_OFF)
        self.ow.reset()
        if rom is None:
            self.ow.writebyte(self.ow.CMD_SKIPROM)
        else:
            self.ow.select_rom(rom)
        self.ow.writebyte(CMD_CONVERT, self.powerpin)

    def read_scratch(self, rom):
        if self.powerpin is not None: # deassert strong pull-up
            self.powerpin(PULLUP_OFF)
        self.ow.reset()
        self.ow.select_rom(rom)
        self.ow.writebyte(CMD_RDSCRATCH)
        self.ow.readinto(self.buf)
        assert self.ow.crc8(self.buf) == 0, 'CRC error'
        return self.buf

    def write_scratch(self, rom, buf):
        if self.powerpin is not None: # deassert strong pull-up
            self.powerpin(PULLUP_OFF)
        self.ow.reset()
        self.ow.select_rom(rom)
        self.ow.writebyte(CMD_WRSCRATCH)
        self.ow.write(buf)

    def read_temp(self, rom):
        try:
            buf = self.read_scratch(rom)
            if rom[0] == 0x10:
                if buf[1]:
                    t = buf[0] >> 1 | 0x80
                    t = -((~t + 1) & 0xff)
                else:
                    t = buf[0] >> 1
                return t - 0.25 + (buf[7] - buf[6]) / buf[7]
            elif rom[0] in (0x22, 0x28):
                t = buf[1] << 8 | buf[0]
                if t & 0x8000: # sign bit set
                    t = -((t ^ 0xffff) + 1)
                return t / 16
            else:
                return None
        except AssertionError:
            return None

    def resolution(self, rom, bits=None):
        if bits is not None and 9 <= bits <= 12:
            self.config[2] = ((bits - 9) << 5) | 0x1f
            self.write_scratch(rom, self.config)
            return bits
        else:
            data = self.read_scratch(rom)
            return ((data[4] >> 5) & 0x03) + 9

    def fahrenheit(self, celsius):
        return celsius * 1.8 + 32 if celsius is not None else None

    def kelvin(self, celsius):
        return celsius + 273.15 if celsius is not None else None

DS18X20-Bibliothek

# DS18x20 temperature sensor driver for MicroPython.
# MIT license; Copyright (c) 2016 Damien P. George

from micropython import const
from machine import Pin

CMD_CONVERT = const(0x44)
CMD_RDSCRATCH = const(0xbe)
CMD_WRSCRATCH = const(0x4e)
CMD_RDPOWER = const(0xb4)
PULLUP_ON = const(1)
PULLUP_OFF = const(0)

class DS18X20:
    def __init__(self, onewire):
        self.ow = onewire
        self.buf = bytearray(9)
        self.config = bytearray(3)
        self.power = 1 # strong power supply by default
        self.powerpin = None

    def powermode(self, powerpin=None):
        if self.powerpin is not None: # deassert strong pull-up
            self.powerpin(PULLUP_OFF)
        self.ow.writebyte_byte(self.ow.CMD_SKIPROM)
        self.ow.writebyte(CMD_RDPOWER)
        self.power = self.ow.readbit()
        if powerpin is not None:
            assert type(powerpin) is Pin, "Parameter must be a Pin object"
            self.powerpin = powerpin
            self.powerpin.init(mode=Pin.OUT, value=0)
        return self.power

    def scan(self):
        if self.powerpin is not None: # deassert strong pull-up
            self.powerpin(PULLUP_OFF)
        return [rom for rom in self.ow.scan() if rom[0] in (0x10, 0x22, 0x28)]

    def convert_temp(self, rom=None):
        if self.powerpin is not None: # deassert strong pull-up
            self.powerpin(PULLUP_OFF)
        self.ow.reset()
        if rom is None:
            self.ow.writebyte(self.ow.CMD_SKIPROM)
        else:
            self.ow.select_rom(rom)
        self.ow.writebyte(CMD_CONVERT, self.powerpin)

    def read_scratch(self, rom):
        if self.powerpin is not None: # deassert strong pull-up
            self.powerpin(PULLUP_OFF)
        self.ow.reset()
        self.ow.select_rom(rom)
        self.ow.writebyte(CMD_RDSCRATCH)
        self.ow.readinto(self.buf)
        assert self.ow.crc8(self.buf) == 0, 'CRC error'
        return self.buf

    def write_scratch(self, rom, buf):
        if self.powerpin is not None: # deassert strong pull-up
            self.powerpin(PULLUP_OFF)
        self.ow.reset()
        self.ow.select_rom(rom)
        self.ow.writebyte(CMD_WRSCRATCH)
        self.ow.write(buf)

    def read_temp(self, rom):
        try:
            buf = self.read_scratch(rom)
            if rom[0] == 0x10:
                if buf[1]:
                    t = buf[0] >> 1 | 0x80
                    t = -((~t + 1) & 0xff)
                else:
                    t = buf[0] >> 1
                return t - 0.25 + (buf[7] - buf[6]) / buf[7]
            elif rom[0] in (0x22, 0x28):
                t = buf[1] << 8 | buf[0]
                if t & 0x8000: # sign bit set
                    t = -((t ^ 0xffff) + 1)
                return t / 16
            else:
                return None
        except AssertionError:
            return None

    def resolution(self, rom, bits=None):
        if bits is not None and 9 <= bits <= 12:
            self.config[2] = ((bits - 9) << 5) | 0x1f
            self.write_scratch(rom, self.config)
            return bits
        else:
            data = self.read_scratch(rom)
            return ((data[4] >> 5) & 0x03) + 9

    def fahrenheit(self, celsius):
        return celsius * 1.8 + 32 if celsius is not None else None

    def kelvin(self, celsius):
        return celsius + 273.15 if celsius is not None else None

Code für den Temperatursensor

from phyphoxBLE import PhyphoxBLE, Experiment
from machine import Pin
from onewire import OneWire
from ds18x20 import DS18X20
import time

pin = Pin(0)
tempSensor = DS18X20(OneWire(pin))
lastTimestamp = 0
blinkInterval = 1000
sensors = tempSensor.scan()
p = PhyphoxBLE()
p.debug = False

def main():
    global lastTimestamp
    p.start("Temperatursensor")
    p._write_callback = receivedData
    plot = Experiment()
    plot.setTitle("Temperaturmessungen")
    plot.setCategory("Micropython Experiments")
    plot.setDescription("Der Microcontroller misst die Temperatur")

    firstView = Experiment.View()
    firstView.setLabel("Temperaturmessung")
    secondView = Experiment.View()
    secondView.setLabel("Einstellungen")
    
    abstand = Experiment.Separator()
    abstand.setHeight(2.0)
    
    #Erste View
    
    firstGraph = Experiment.Graph()
    firstGraph.setLabel("Temperaturmessungen")
    firstGraph.setUnitY("°C")
    firstGraph.setLabelY("Temperatur")
    firstGraph.setXPrecision(1)
    firstGraph.setYPrecision(2)
    firstGraph.setChannel(0,1)
    
    secondGraph = Experiment.Graph()
    secondGraph.setUnitY("°C")
    secondGraph.setLabelY("Temperatur")
    secondGraph.setXPrecision(1)
    secondGraph.setYPrecision(2)
    secondGraph.setChannel(0,2)
    
    thirdGraph = Experiment.Graph()
    thirdGraph.setUnitY("°C")
    thirdGraph.setLabelY("Temperatur")
    thirdGraph.setUnitX("s")
    thirdGraph.setLabelX("Zeit")
    thirdGraph.setXPrecision(1)
    thirdGraph.setYPrecision(2)
    thirdGraph.setChannel(0,3)
    
    mySet = Experiment.ExportSet()
    mySet.setLabel("Temperaturmessungen")

    myData1 = Experiment.ExportData() 
    myData1.setLabel("Zeit")
    myData1.setDatachannel(0)

    myData2 = Experiment.ExportData() 
    myData2.setLabel("Temperatur Celsius")
    myData2.setDatachannel(1)
    
    myData3 = Experiment.ExportData() 
    myData3.setLabel("Temperatur Kelvin")
    myData3.setDatachannel(2)
    
    Interval = Experiment.Edit() 
    Interval.setLabel("Gewünschter Interval:")
    Interval.setUnit("s")
    Interval.setSigned(False)
    Interval.setDecimal(False)
    Interval.setChannel(1)
    Interval.setXMLAttribute("min=\"1\"")
    
    myValue = Experiment.Value()
    myValue.setLabel("Aktueller Interval:")
    myValue.setPrecision(0)
    myValue.setUnit("s")
    myValue.setColor("FFFFFF")
    myValue.setChannel(4)
    myValue.setXMLAttribute("size=\"2\"")
    
    firstView.addElement(abstand)
    firstView.addElement(firstGraph)
    firstView.addElement(secondGraph)
    firstView.addElement(thirdGraph)
    secondView.addElement(abstand)
    secondView.addElement(myValue)
    secondView.addElement(Interval)
    plot.addView(firstView)
    plot.addView(secondView)
    mySet.addElement(myData1)
    mySet.addElement(myData2)
    mySet.addElement(myData3)
    plot.addExportSet(mySet)
    p.addExperiment(plot)
    
    while True:
        
        if time.ticks_ms()-lastTimestamp > blinkInterval:
            lastTimestamp = time.ticks_ms()
            tempSensor.convert_temp()
            digits = 2
            for sensor in sensors:
                temp = tempSensor.read_temp(sensor)
                if temp is not None and type(temp) == float:
                    if sensor == sensors[0]:
                        tempCelsius1 = round(temp, digits)
                    elif sensor == sensors[1]:
                        tempCelsius2 = round(temp, digits)
                    elif sensor == sensors[2]:
                        tempCelsius3 = round(temp, digits)

            p.write(tempCelsius1,tempCelsius2,tempCelsius3,blinkInterval/1000)

def receivedData():
    global blinkInterval
    receivedInterval = p.read()
    if receivedInterval > 0 and receivedInterval != blinkInterval:
        print("New Interval: ", receivedInterval)
        blinkInterval = receivedInterval*1000

if __name__ == "__main__":
    main()

CO2-Sensor

SCD30-Bibliothek

from machine import I2C
import utime
import struct

class SCD30:

    class NotFoundException(Exception):
        pass

    class CRCException(Exception):
        pass

    START_CONT_MEASURE = 0x0010
    STOP_CONT_MEASURE = 0x0104
    SET_MEASURE_INTERVAL = 0x4600
    GET_STATUS_READY = 0x0202
    READ_MEASUREMENT = 0x0300
    SET_ASC = 0x5306
    SET_FRC = 0x5204
    SET_TEMP_OFFSET = 0x5403
    SET_ALT_COMP = 0x5102
    GET_FIRMWARE_VER = 0xd100
    SOFT_RESET = 0xd304

    CLOCK_TIME_US = 10

    # Generated using
    # crc_table = []
    # for crc in range(256):
    #     for crc_bit in range(8):
    #         if crc & 0x80:
    #             crc = (crc << 1) ^ CRC8_POLYNOMIAL;
    #         else:
    #             crc = (crc << 1);
    #         crc = crc%256
    #     crc_table.append(crc)

    CRC_TABLE = [
        0, 49, 98, 83, 196, 245, 166, 151, 185, 136, 219, 234, 125, 76, 31, 46,
        67, 114, 33, 16, 135, 182, 229, 212, 250, 203, 152, 169, 62, 15, 92, 109,
        134, 183, 228, 213, 66, 115, 32, 17, 63, 14, 93, 108, 251, 202, 153, 168,
        197, 244, 167, 150, 1, 48, 99, 82, 124, 77, 30, 47, 184, 137, 218, 235,
        61, 12, 95, 110, 249, 200, 155, 170, 132, 181, 230, 215, 64, 113, 34, 19,
        126, 79, 28, 45, 186, 139, 216, 233, 199, 246, 165, 148, 3, 50, 97, 80,
        187, 138, 217, 232, 127, 78, 29, 44, 2, 51, 96, 81, 198, 247, 164, 149,
        248, 201, 154, 171, 60, 13, 94, 111, 65, 112, 35, 18, 133, 180, 231, 214,
        122, 75, 24, 41, 190, 143, 220, 237, 195, 242, 161, 144, 7, 54, 101, 84,
        57, 8, 91, 106, 253, 204, 159, 174, 128, 177, 226, 211, 68, 117, 38, 23,
        252, 205, 158, 175, 56, 9, 90, 107, 69, 116, 39, 22, 129, 176, 227, 210,
        191, 142, 221, 236, 123, 74, 25, 40, 6, 55, 100, 85, 194, 243, 160, 145,
        71, 118, 37, 20, 131, 178, 225, 208, 254, 207, 156, 173, 58, 11, 88, 105,
        4, 53, 102, 87, 192, 241, 162, 147, 189, 140, 223, 238, 121, 72, 27, 42,
        193, 240, 163, 146, 5, 52, 103, 86, 120, 73, 26, 43, 188, 141, 222, 239,
        130, 179, 224, 209, 70, 119, 36, 21, 59, 10, 89, 104, 255, 206, 157, 172
        ]

    def __init__(self, i2c, addr, pause=1000):
        self.i2c = i2c
        self.pause = pause
        self.addr = addr
        if not addr in i2c.scan():
            raise self.NotFoundException

    def start_continous_measurement(self, ambient_pressure=0):
        bint = struct.pack('>H', ambient_pressure)
        crc = self.__crc(bint[0], bint[1])
        data = bint + bytes([crc])
        self.i2c.writeto_mem(self.addr, self.START_CONT_MEASURE, data, addrsize=16)

    def stop_continous_measurement(self):
        self.__write_command(self.STOP_CONT_MEASURE)

    def soft_reset(self):
        self.__write_command(self.SOFT_RESET)

    def get_firmware_version(self):
        ver = self.__read_bytes(self.GET_FIRMWARE_VER, 3)
        self.__check_crc(ver)
        return struct.unpack('BB', ver)

    def read_measurement(self):
        measurement = self.__read_bytes(self.READ_MEASUREMENT, 18)
        for i in range(0, len(measurement), 3):
            self.__check_crc(measurement[i:i+3])

        value = measurement[0:]
        co2 = struct.unpack('>f', value[0:2] + value[3:5])[0]
        value = measurement[6:]
        temperature = struct.unpack('>f', value[0:2] + value[3:5])[0]
        value = measurement[12:]
        relh = struct.unpack('>f', value[0:2] + value[3:5])[0]
        return (co2, temperature, relh)

    def get_status_ready(self):
        ready = self.__read_bytes(self.GET_STATUS_READY, 3)
        self.__check_crc(ready)
        return struct.unpack('>H', ready)[0]

    def get_measurement_interval(self):
        bint = self.__read_bytes(self.SET_MEASURE_INTERVAL, 3)
        self.__check_crc(bint)
        return struct.unpack('>H', bint)[0]

    def set_measurement_interval(self, interval):
        bint = struct.pack('>H', interval)
        crc = self.__crc(bint[0], bint[1])
        data = bint + bytes([crc])
        self.i2c.writeto_mem(self.addr, self.SET_MEASURE_INTERVAL, data, addrsize=16)

    def get_automatic_recalibration(self):
        bint = self.__read_bytes(self.SET_ASC, 3)
        self.__check_crc(bint)
        return struct.unpack('>H', bint)[0] == 1

    def set_automatic_recalibration(self, enable):
        bint = struct.pack('>H', 1 if enable else 0)
        crc = self.__crc(bint[0], bint[1])
        data = bint + bytes([crc])
        self.i2c.writeto_mem(self.addr, self.SET_FRC, data, addrsize=16)

    def get_forced_recalibration(self):
        bint = self.__read_bytes(self.SET_FRC, 3)
        self.__check_crc(bint)
        return struct.unpack('>H', bint)[0]

    def set_forced_recalibration(self, co2ppm):
        bint = struct.pack('>H', co2ppm)
        crc = self.__crc(bint[0], bint[1])
        data = bint + bytes([crc])
        self.i2c.writeto_mem(self.addr, self.SET_FRC, data, addrsize=16)

    def get_temperature_offset(self):
        bint = self.__read_bytes(self.SET_TEMP_OFFSET, 3)
        self.__check_crc(bint)
        return struct.unpack('>H', bint)[0] / 100.0

    def set_temperature_offset(self, offset):
        bint = struct.pack('>H', int(offset * 100))
        crc = self.__crc(bint[0], bint[1])
        data = bint + bytes([crc])
        self.i2c.writeto_mem(self.addr, self.SET_TEMP_OFFSET, data, addrsize=16)

    def get_altitude_comp(self):
        bint = self.__read_bytes(self.SET_ALT_COMP, 3)
        self.__check_crc(bint)
        return struct.unpack('>H', bint)[0]

    def set_altitude_comp(self, altitude):
        bint = struct.pack('>H', altitude)
        crc = self.__crc(bint[0], bint[1])
        data = bint + bytes([crc])
        self.i2c.writeto_mem(self.addr, self.SET_ALT_COMP, data, addrsize=16)

    def __write_command(self, cmd):
        bcmd = struct.pack('>H', cmd)
        self.i2c.writeto(self.addr, bcmd)

    def __read_bytes(self, cmd, count):
        self.__write_command(cmd)
        utime.sleep_us(self.pause)
        return self.i2c.readfrom(self.addr, count)

    def __check_crc(self, arr):
        assert (len(arr) == 3)
        if self.__crc(arr[0], arr[1]) != arr[2]:
            raise self.CRCException

    def __crc(self, msb, lsb):
        crc = 0xff
        crc ^= msb
        crc = self.CRC_TABLE[crc]
        if lsb is not None:
            crc ^= lsb
            crc = self.CRC_TABLE[crc]
        return crc

SCD30-Code

from phyphoxBLE import PhyphoxBLE, Experiment
from machine import Pin, SoftI2C
from scd30 import SCD30
from machine import Pin, I2C
from time import sleep_ms

i2c = SoftI2C(sda=Pin(21), scl=Pin(22))
readyPin = Pin(23, Pin.IN, Pin.PULL_UP)
devices = i2c.scan()

for device in devices:
    if device == 0x61:
        scd30_address = device
        break

scd30_sensor = SCD30(i2c, scd30_address)
scd30_sensor.start_continous_measurement()

blinkInterval = 300000
lastTimestamp = 0
p = PhyphoxBLE()
p.debug = False

def main():
    global lastTimestamp
    p.start("CO2-Sensor")
    p._write_callback = receivedData
    plot = Experiment()
    plot.setTitle("CO2-Messung")
    plot.setCategory("Micropython Experiments")
    plot.setDescription("Der Microcontroller misst den CO2-Gehalt in der Luft")

    firstView = Experiment.View()
    firstView.setLabel("CO2-Messung")
    secondView = Experiment.View()
    secondView.setLabel("Einstellungen")
    
    abstand = Experiment.Separator()
    abstand.setHeight(2.0)
    
    #Erste View
    
    firstGraph = Experiment.Graph()
    firstGraph.setLabel("CO2-Messung")
    firstGraph.setUnitY("ppm")
    firstGraph.setLabelY("Konzentration")
    firstGraph.setXPrecision(1)
    firstGraph.setYPrecision(2)
    firstGraph.setChannel(0,1)
    
    secondGraph = Experiment.Graph()
    secondGraph.setUnitY("°C")
    secondGraph.setLabelY("Temperatur")
    secondGraph.setXPrecision(1)
    secondGraph.setYPrecision(2)
    secondGraph.setChannel(0,2)
    
    thirdGraph = Experiment.Graph()
    thirdGraph.setUnitY("%")
    thirdGraph.setLabelY("Luftfeuchtigkeit")
    thirdGraph.setUnitX("s")
    thirdGraph.setLabelX("Zeit")
    thirdGraph.setXPrecision(1)
    thirdGraph.setYPrecision(2)
    thirdGraph.setChannel(0,3)
    
    mySet = Experiment.ExportSet()
    mySet.setLabel("CO2-Messungen")

    myData1 = Experiment.ExportData() 
    myData1.setLabel("Zeit")
    myData1.setDatachannel(0)

    myData2 = Experiment.ExportData() 
    myData2.setLabel("CO2-Konzentration")
    myData2.setDatachannel(1)
    
    myData3 = Experiment.ExportData() 
    myData3.setLabel("Temperatur")
    myData3.setDatachannel(2)
    
    myData4 = Experiment.ExportData() 
    myData4.setLabel("Luftfeuchtigkeit")
    myData4.setDatachannel(3)
    
    Interval = Experiment.Edit() 
    Interval.setLabel("Gewünschter Interval:")
    Interval.setUnit("s")
    Interval.setSigned(False)
    Interval.setDecimal(False)
    Interval.setChannel(1)
    Interval.setXMLAttribute("min=\"1\"")
    
    myValue = Experiment.Value()
    myValue.setLabel("Aktueller Interval:")
    myValue.setPrecision(0)
    myValue.setUnit("s")
    myValue.setColor("FFFFFF")
    myValue.setChannel(4)
    myValue.setXMLAttribute("size=\"2\"")
    
    firstView.addElement(abstand)
    firstView.addElement(firstGraph)
    firstView.addElement(secondGraph)
    firstView.addElement(thirdGraph)
    secondView.addElement(abstand)
    secondView.addElement(myValue)
    secondView.addElement(Interval)
    plot.addView(firstView)
    plot.addView(secondView)
    mySet.addElement(myData1)
    mySet.addElement(myData2)
    mySet.addElement(myData3)
    mySet.addElement(myData4)
    plot.addExportSet(mySet)
    p.addExperiment(plot)
    
    while True:
        while readyPin.value() != 1:
            sleep_ms(200)
        # Lese die Sensordaten
        co2, temperature, humidity = scd30_sensor.read_measurement()

        # Runde die Werte auf zwei Nachkommastellen
        co2 = round(co2, 2)
        temperature = round(temperature, 2)
        humidity = round(humidity, 2)
        
        
        # Gib die Werte aus
        print("******************************")
        print("CO2: {} ppm".format(co2))
        print("Temperatur: {} °C".format(temperature))
        print("Luftfeuchtigkeit: {} %".format(humidity))
        
        p.write(co2,temperature,humidity,blinkInterval/1000)
            
def receivedData():
    global blinkInterval
    receivedInterval = p.read()
    if receivedInterval > 0 and receivedInterval != blinkInterval:
        print("New Interval: ", receivedInterval)
        blinkInterval = receivedInterval*1000
        
if __name__ == "__main__":
    main()