#author("2024-10-25T13:07:38+09:00","default:BotComputing-WikiIoT","BotComputing-WikiIoT")
#author("2024-10-28T10:50:37+09:00","default:BotComputing-WikiIoT","BotComputing-WikiIoT")
[[wiki_bot_02.py]]

#code(Python){{
# -*- coding: utf-8 -*-
#
# 2024 10/25
#
from machine import UART,Pin,ADC, Timer
import micropython,time,gc
# for analog co2 sensor, gravity:analog infrared co2 sensor
# 20241004 
# for analog co2 sensor, gravity:analog infrared co2 sensor+smell
# 20241028
 
# Updated 2018 and 2020
# This module is based on the below cited resources, which are all
# based on the documentation as provided in the Bosch Data Sheet and
# the sample implementation provided therein.
#
# Final Document: BST-BME280-DS002-15
#
# Authors: Paul Cunnane 2016, Peter Dahlebrg 2016
#
# This module borrows from the Adafruit BME280 Python library. Original
# Copyright notices are reproduced below.
#
# Those libraries were written for the Raspberry Pi. This modification is
# intended for the MicroPython and esp8266 boards.
#
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Based on the BMP280 driver with BME280 changes provided by
# David J Taylor, Edinburgh (www.satsignal.eu)
#
# Based on Adafruit_I2C.py created by Kevin Townsend.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
 
from ustruct import unpack, unpack_from
from array import array
 
# BME280 default address.
BME280_I2CADDR = 0x76
 
# Operating Modes
BME280_OSAMPLE_1 = 1
BME280_OSAMPLE_2 = 2
BME280_OSAMPLE_4 = 3
BME280_OSAMPLE_8 = 4
BME280_OSAMPLE_16 = 5
 
BME280_REGISTER_CONTROL_HUM = 0xF2
BME280_REGISTER_STATUS = 0xF3
BME280_REGISTER_CONTROL = 0xF4
 
MODE_SLEEP = const(0)
MODE_FORCED = const(1)
MODE_NORMAL = const(3)
 
BME280_TIMEOUT = const(100)  # about 1 second timeout
 
 
class BME280:
 
    def __init__(self,
                 mode=BME280_OSAMPLE_8,
                 address=BME280_I2CADDR,
                 i2c=None,
                 **kwargs):
        # Check that mode is valid.
        if type(mode) is tuple and len(mode) == 3:
            self._mode_hum, self._mode_temp, self._mode_press = mode
        elif type(mode) == int:
            self._mode_hum, self._mode_temp, self._mode_press = mode, mode, mode
        else:
            raise ValueError("Wrong type for the mode parameter, must be int or a 3 element tuple")
 
        for mode in (self._mode_hum, self._mode_temp, self._mode_press):
            if mode not in [BME280_OSAMPLE_1, BME280_OSAMPLE_2, BME280_OSAMPLE_4,
                            BME280_OSAMPLE_8, BME280_OSAMPLE_16]:
                raise ValueError(
                    'Unexpected mode value {0}. Set mode to one of '
                    'BME280_ULTRALOWPOWER, BME280_STANDARD, BME280_HIGHRES, or '
                    'BME280_ULTRAHIGHRES'.format(mode))
 
        self.address = address
        if i2c is None:
            raise ValueError('An I2C object is required.')
        self.i2c = i2c
        self.__sealevel = 101325
 
        # load calibration data
        dig_88_a1 = self.i2c.readfrom_mem(self.address, 0x88, 26)
        dig_e1_e7 = self.i2c.readfrom_mem(self.address, 0xE1, 7)
        self.dig_T1, self.dig_T2, self.dig_T3, self.dig_P1, \
            self.dig_P2, self.dig_P3, self.dig_P4, self.dig_P5, \
            self.dig_P6, self.dig_P7, self.dig_P8, self.dig_P9, \
            _, self.dig_H1 = unpack("<HhhHhhhhhhhhBB", dig_88_a1)
 
        self.dig_H2, self.dig_H3, self.dig_H4,\
            self.dig_H5, self.dig_H6 = unpack("<hBbhb", dig_e1_e7)
        # unfold H4, H5, keeping care of a potential sign
        self.dig_H4 = (self.dig_H4 * 16) + (self.dig_H5 & 0xF)
        self.dig_H5 //= 16
 
        self.t_fine = 0
 
        # temporary data holders which stay allocated
        self._l1_barray = bytearray(1)
        self._l8_barray = bytearray(8)
        self._l3_resultarray = array("i", [0, 0, 0])
 
        self._l1_barray[0] = self._mode_temp << 5 | self._mode_press << 2 | MODE_SLEEP
        self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL,
                             bytearray([0x3c | MODE_SLEEP]))
 
    def read_raw_data(self, result):
        """ Reads the raw (uncompensated) data from the sensor.
 
            Args:
                result: array of length 3 or alike where the result will be
                stored, in temperature, pressure, humidity order
            Returns:
                None
        """
 
        self._l1_barray[0] = self._mode_hum
        self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL_HUM,
                             self._l1_barray)
        self._l1_barray[0] = self._mode_temp << 5 | self._mode_press << 2 | MODE_FORCED
        self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL,
                             self._l1_barray)
 
        # Wait for conversion to complete
        for _ in range(BME280_TIMEOUT):
            if self.i2c.readfrom_mem(self.address, BME280_REGISTER_STATUS, 1)[0] & 0x08:
                time.sleep_ms(10)  # still busy
            else:
                break  # Sensor ready
        else:
            raise RuntimeError("Sensor BME280 not ready")
 
        # burst readout from 0xF7 to 0xFE, recommended by datasheet
        self.i2c.readfrom_mem_into(self.address, 0xF7, self._l8_barray)
        readout = self._l8_barray
        # pressure(0xF7): ((msb << 16) | (lsb << 8) | xlsb) >> 4
        raw_press = ((readout[0] << 16) | (readout[1] << 8) | readout[2]) >> 4
        # temperature(0xFA): ((msb << 16) | (lsb << 8) | xlsb) >> 4
        raw_temp = ((readout[3] << 16) | (readout[4] << 8) | readout[5]) >> 4
        # humidity(0xFD): (msb << 8) | lsb
        raw_hum = (readout[6] << 8) | readout[7]
 
        result[0] = raw_temp
        result[1] = raw_press
        result[2] = raw_hum
 
    def read_compensated_data(self, result=None):
        """ Reads the data from the sensor and returns the compensated data.
 
            Args:
                result: array of length 3 or alike where the result will be
                stored, in temperature, pressure, humidity order. You may use
                this to read out the sensor without allocating heap memory
 
            Returns:
                array with temperature, pressure, humidity. Will be the one
                from the result parameter if not None
        """
        self.read_raw_data(self._l3_resultarray)
        raw_temp, raw_press, raw_hum = self._l3_resultarray
        # temperature
        var1 = (((raw_temp // 8) - (self.dig_T1 * 2)) * self.dig_T2) // 2048
        var2 = (raw_temp // 16) - self.dig_T1
        var2 = (((var2 * var2) // 4096) * self.dig_T3) // 16384
        self.t_fine = var1 + var2
        temp = (self.t_fine * 5 + 128) // 256
 
        # pressure
        var1 = self.t_fine - 128000
        var2 = var1 * var1 * self.dig_P6
        var2 = var2 + ((var1 * self.dig_P5) << 17)
        var2 = var2 + (self.dig_P4 << 35)
        var1 = (((var1 * var1 * self.dig_P3) >> 8) +
                ((var1 * self.dig_P2) << 12))
        var1 = (((1 << 47) + var1) * self.dig_P1) >> 33
        if var1 == 0:
            pressure = 0
        else:
            p = ((((1048576 - raw_press) << 31) - var2) * 3125) // var1
            var1 = (self.dig_P9 * (p >> 13) * (p >> 13)) >> 25
            var2 = (self.dig_P8 * p) >> 19
            pressure = ((p + var1 + var2) >> 8) + (self.dig_P7 << 4)
 
        # humidity
        h = self.t_fine - 76800
        h = (((((raw_hum << 14) - (self.dig_H4 << 20) -
                (self.dig_H5 * h)) + 16384) >> 15) *
             (((((((h * self.dig_H6) >> 10) *
                (((h * self.dig_H3) >> 11) + 32768)) >> 10) + 2097152) *
              self.dig_H2 + 8192) >> 14))
        h = h - (((((h >> 15) * (h >> 15)) >> 7) * self.dig_H1) >> 4)
        h = 0 if h < 0 else h
        h = 419430400 if h > 419430400 else h
        humidity = h >> 12
        if humidity < 0:
            humidity = 0
        if humidity > 100 * 1024:
            humidity = 100 * 1024
 
        if result:
            result[0] = temp
            result[1] = pressure
            result[2] = humidity
            return result
 
        return array("i", (temp, pressure, humidity))
 
    @property
    def sealevel(self):
        return self.__sealevel
 
    @sealevel.setter
    def sealevel(self, value):
        if 30000 < value < 120000:  # just ensure some reasonable value
            self.__sealevel = value
 
    @property
    def altitude(self):
        '''
        Altitude in m.
        '''
        from math import pow
        try:
            p = 44330 * (1.0 - pow((self.read_compensated_data()[1] / 256) /
                                   self.__sealevel, 0.1903))
        except:
            p = 0.0
        return p
 
    @property
    def dew_point(self):
        """
        Compute the dew point temperature for the current Temperature
        and Humidity measured pair
        """
        from math import log
        t, p, h = self.read_compensated_data()
        t /= 100
        h /= 1024
        h = (log(h, 10) - 2) / 0.4343 + (17.62 * t) / (243.12 + t)
        return (243.12 * h / (17.62 - h)) * 100
 
    @property
    def values(self):
        """ human readable values """
 
        t, p, h = self.read_compensated_data()
 
        p = p / 256
 
        h = h / 1024
        return ("{}C".format(t / 100), "{:.02f}hPa".format(p/100),
                "{:.02f}%".format(h))
 
class Sensing:
 
    def get_co2(self):
        return self.co2_value
    def get_motion(self):
        return self.motion_value
    def get_lx(self):
        return self.lx_value
    def get_pressure(self):
        return self.pressure_value
    def get_temperature(self):
        return self.temperature_value
    def get_humidity(self):
        return self.humidity_value
    def co2_read(self):
        ##print("start read co2")
        ##global o_b, r_b, co2_value
        #self.o_b = bytearray([0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79])
        #self.r_b=bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ])
        #self.u.write(self.o_b)
        #time.sleep(5)
        #result = self.u.readinto(self.r_b,9)
        ##print(result)
        #v=(int(self.r_b[2])*256)+int(self.r_b[3])
        ##print("co2:"+str(v))
        #self.co2_value = v
        adcVal = self.co2.read_u16()
        vx = float(adcVal)*(3.3/65536.0)
        if vx == 0:
            #print("A problem has occurred with the sensor.")
            self.co2_value=0
        elif vx < 0.4:
            #print("Pre-heating the sensor...")
            self.co2_value=0
        else:
            vd=vx-0.4
            concentration=(vd*5000.0)/1.6
            self.co2_value = int(concentration)
    def get_smell(self):
        return self.smell_value
 
    motion_counter = 0
    motion_accum = 0
    def motion_read(self):
        #print("start read motion")
        #global motion_counter, motion_accum, motion_value
        if self.motion_counter < 100:
            self.motion_counter = self.motion_counter + 1
            self.motion_accum = self.motion_accum + self.pir.value()
        else:
            self.motion_value = self.motion_accum
            self.motion_counter = 0
            self.motion_accum = 0
            #print("motion:"+str(self.motion_value))    
 
    def lx_read(self):
        #global lx_value
        self.lx_value = self.lx.read_u16()
        #print("lx:"+str(self.lx_value))
    def smell_read(self):
        #global lx_value
        self.smell_value = self.smell.read_u16()
        #print("lx:"+str(self.lx_value))

    def pressure_read(self):
        #global pressure_value
        self.pressure_value = self.bme.values[1]
        #print("pressure:"+self.pressure_value)
 
    def temperature_read(self):
        #global temperature_value
        self.temperature_value = self.bme.values[0]
        #print("temperature:"+self.temperature_value)
 
    def humidity_read(self):
        #global humidity_value
        self.humidity_value = self.bme.values[2]
        #print("humidity:"+self.humidity_value)

    def smell_capture(self):
        self.smell_a0.value(1)
        time.sleep(0.001)
        self.smell_read()
        time.sleep(0.001)
        self.smell_a0.value(0)
        print("smell="+str(self.smell_value))
 
    def sensor_read_one_minute(self,timer):
        self.co2_read()
        self.motion_read()
        #self.motion_read()
        self.lx_read()
        self.temperature_read()
        self.pressure_read()
        self.humidity_read()
        self.smell_read()
 
    def sensor_read_one_second(self,timer):
    def sensor_read_100ms(self,timer):
        if self.c100ms>=60:
            self.c100ms=0
        if self.c100ms%6==0:
            self.motion_read()
        if self.c100ms%10==0:
            self.smell_capture()
        self.c100ms=self.c100ms+1
        #print("start sensor read one second")
        self.motion_read()
    def reset_pico(self,timer):
        machine.reset()
        
    #def sensor_read_1s(self,timer):
    #
 
    def __init__(self):
        #print ("start setup")
        self.u = UART(1, baudrate=9600, tx=Pin(8), rx=Pin(9)) 
        #print('exception buf')
        #interrupt exception
        micropython.alloc_emergency_exception_buf(100)
        #print('otimer')
        self.one_minute_timer = Timer()
        self.one_second_timer = Timer()
        self.one_hour_timer = Timer()
        #self.timer_1s = Timer()
        self.timer_100ms = Timer()
        #print('adc')
        self.pir=Pin(14, Pin.IN)
        self.lx = ADC(Pin(27))
        self.co2 = ADC(Pin(28))
        self.smell = ADC(Pin(26))
        self.smell_a0=Pin(15,Pin.OUT)
        #print('i2c')
        self.sda=machine.Pin(0)
        self.scl=machine.Pin(1)
        #print('i2c2')
        self.i2c = machine.I2C(0, sda = self.sda, scl = self.scl, freq = 100000)
        #print('bme280')
        self.bme = BME280(i2c = self.i2c)
        #print('x')
        self.co2_value = 0
        self.motion_value = 0
        self.smell_value = 0
        self.lx_value = 0
        self.pressure_value = ""
        self.temperature_value = ""
        self.humidity_value = ""
        self.one_hour_timer.init(period=3600000,mode=Timer.PERIODIC, callback=self.reset_pico)
        #self.one_hour_timer.init(period=600000,mode=Timer.PERIODIC, callback=self.reset_pico)
        self.one_minute_timer.init(period=60000, mode=Timer.PERIODIC, callback=self.sensor_read_one_minute)
        self.one_second_timer.init(period=600, mode=Timer.PERIODIC, callback=self.sensor_read_one_second)
        self.timer_100ms.init(period=100, mode=Timer.PERIODIC, callback=self.sensor_read_100ms)
        #self.timer_1s.init(period=1000,mode=Timer.PERIODIC, callback=self.sensor_read_1s)
        ##time.sleep(60)
        #print ("end setup")
        self.c100ms=0
 
class Parser:
    def __init__(self,line,sensing):
        self.line=line
        self.sensing=sensing
    def p_key(self,key,p):
        #print("p_key key="+key+" p="+str(p)+" "+self.html[p:p+10])
        keylen=len(key)
        if p+keylen>=len(self.line):
            return p
        q=p
        if self.line[q:q+keylen]==key:  
            #print("key="+key+" have found, q="+str(q+keylen))
            return q+keylen
        #print("key="+key+" is not found, p="+str(p))
        return p 
      
    def p_String_Constant(self,p,strc):
        #print("p_String_Constant p="+str(p)+' '+self.html[p:p+10])
        rx=[""]
        xstr=""
        pw=p
        q=self.p_key('"',p)
        if p!=q:
            #print("p_String_Constant p="+str(p)+' '+self.html[p:p+10])
            p=q
            fx=self.line[p]
            xlen=len(self.line)-p
            while fx!='"':
                if xlen==0:
                    return pw
                if fx in ['\n','\t','\r']:
                    return pw
                if fx=='\\':
                   p=p+1
                p=p+1
                fx=self.line[p]
                xlen=len(self.line)-p
            q=self.p_key('"',p)
            if p!=q:
                strc[0]=self.line[pw+1:q-1]
                #print("p_string_constant, strc="+str(strc[0]))
                return q
        #print("p_String_Constant, fail to find \"")
        return pw
    def p_Date(self,p,date_str):
        #print("p_Date p="+str(p)+' '+self.html[p:p+10])
        rx=[""]
        xstr=""
        pw=p
        year=[0]
        q=self.p_number(p,year)
        if p==q:
            return pw
        p=q
        q=self.p_key('/',p)
        if p==q:
            return pw
        p=q
        month=[0]
        q=self.p_number(p,month)
        if p==q:
            return pw
        p=q
        q=self.p_key('/',p)
        if p==q:
            return pw
        p=q
        day=[0]
        q=self.p_number(p,day)
        if p==q:
            return False
        p=q
        q=self.p_b(p)
        if p==q:
            return pw
        p=q
        hour=[0]
        q=self.p_number(p,hour)
        if p==q:
            return pw
        p=q
        q=self.p_key(':',p)
        if p==q:
            return pw
        p=q
        minute=[0]
        q=self.p_number(p,minute)
        if p==q:
            return pw
        p=q
        q=self.p_key(':',p)
        if p==q:
            return pw
        p=q
        second=[0]
        q=self.p_number(p,second)
        if p==q:
            return pw
        p=q
        date_str[0]=str(year[0])+'/'+str(month[0])+'/'+str(day[0])+' '+str(hour[0])+':'+str(minute[0])+':'+str(second[0])
        return p
    
    def parse_the_line(self):
      p=0
      p=self.p_b(p)
      q=self.p_key('ex',p)
      if q>p:
            p=q
            p=self.p_b(p)
            exp=self.line[p:]
            #print(exp)
            exec_locals={'self':self}
            try:
                rtn=eval(exp,globals(),exec_locals)
                print(rtn)
            except Exception as e:
                print("error in "+exp)
                print(e)
    def p_b(self,p):
        #print("parse_b")
        #print("p="+str(p))
        xlen=len(self.line)
        if p>=xlen:
            return p
        while self.line[p]==' ':
            p=p+1
            if p>=xlen:
                p=xlen
                break
        return p
 
    def p_name(self,p,name):
        #print("p_name, self.html="+ self.html[p:p+10])
        #print("p="+str(p)+"..."+self.html[p:p+10])
        q=p
        last_p=len(self.line)
        while self.line[q] in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_':
            #print("self.html["+str(q)+"]="+self.html[q])
            q=q+1
            if q>=last_p:
                break
        name[0]=self.line[p:q]
        #print("p_name p="+str(p)+" q="+str(q)+" name="+str(name[0]))
        #print("p="+str(p)+" q="+str(q)+" name="+name[0])
        #print("name[0]= "+name[0])
        return q
    def p_number(self,p,num):
        #print("p_number")
        #print("p="+str(p))
        n=""
        last_p=len(self.line)
        q=p
        while self.line[p] in '0123456789':
            q=q+1
            if q>=last_p:
                break
        num[0]=int(self.line[p:q])
        #print("p_number p="+str(p)+" q="+str(q)+" num="+str(num[0]))
        return q
    def p_an_equation(self,p,eqs):
        #print("p_an_equation p="+str(p)+' '+self.html[p:p+10])
        pw=p
        p=self.p_b(p)
        nx=[""]
        strc=[""]
        q=self.p_name(p,nx)
        #print("p_an_equation name="+nx[0])
        if p==q:
            #print("p_an_equation fail to find name")
            return pw
        p=q
        p=self.p_b(p)
        q=self.p_key('=',p)
        if p==q:
            #print("p_an_equation fail to find =")
            return pw
        p=q
        p=self.p_b(p)
        q=self.p_String_Constant(p,strc)
        if p==q:
            num=[0]
            q=self.p_number(p,num)
            if p==q:
                return pw
            else:
                eqs[nx[0]]=num[0]
                #print("p_qn_equation get "+nx[0]+"="+str(num[0]))
                return q
        else:
            eqs[nx[0]]=strc[0]
            #print("p_an_equation get "+nx[0]+"="+str(strc[0]))
            return q
    def p_equations(self,p,eqs):
        #print("parse_equations")
        #print("p="+str(p))
        pw=p
        while True:
            q=self.p_an_equation(p,eqs)
            if p==q:
                return p
            p=q
    def p_get_equations_of_the_tag(self,p,tag_name,eqs):
        #print("p_get_equations_of_the_tag <"+tag_name)
        #print("p="+str(p)+' '+self.html[p:p+10])
        pw=p
        try:
            q=self.line.index('<'+tag_name,pw)
        except:
            #print("p_get_equation_of_the_tag fail to find <"+tag_name)
            return p
        #print("q="+str(q)+' '+self.html[q:q+10])
        q=q+len('<'+tag_name)
        #print("q="+str(q)+' '+self.html[q:q+10])
        if pw==q:
            #print("p_get_equation_of_the_tag fail to find <"+tag_name)
            return p
        pw=q
        q=self.p_equations(pw,eqs)
        #print("after equations q="+str(q) +' '+self.html[q:q+10])
        if pw==q:
            return p
        pw=q
        pw=self.p_b(pw)
        #print("pw="+str(pw))
        q=self.p_key('>',pw)
        #print("after > q="+str(q)+' '+self.html[q:q+10])
        if pw!=q:
            return q
        q=self.p_key('/>',pw)
        if pw!=q:
            return q
        return p
 
if __name__ == '__main__':
    #
    # ex self.sensing.get_co2()
    # ex self.sensing.get_motion()
    # ex self.sensing.get_lx()
    # ex self.sensing.get_pressure()
    # ex self.sensing.get_temperature()
    # ex self.sensing.get_humidity()
    #
    sensing=Sensing()
    while True:
        line=input()
        parser=Parser(line,sensing)
        parser.parse_the_line()
        gc.collect()
        time.sleep(0.1)
}}

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS