温湿度传感器 气压 气体

https://gitee.com/mirrors_robert-hh/bme680-micropython

BME680 全包

后期

BMP280+AHT10

SGP-41 氮氧化合物 CO气体检测

发表在 None | 留下评论

蓝牙

广播 100ms 间隔高速率

import bluetooth, struct


#汉字一个3B,注意 最大8个汉字  100ms 广播高速率
def ble_name(dev_name):
    if len(dev_name)>26:
        return False
    ble = bluetooth.BLE()
    ble.active(False)
    ble.active(True)
    adv_data = struct.pack('BBB', 0x02, 0x01, 0x06)
    name_bytes = dev_name.encode()
    adv_data += struct.pack('B', 1 + len(name_bytes)) + b'\x09' + name_bytes
    ble.gap_advertise(None)
    ble.gap_advertise(100000, adv_data)
    print("广播启动,名称:", dev_name," 长度:",len(dev_name))
    return True

扫描 1s 定时版

from micropython import const
import bluetooth
import time

# 常量定义
_IRQ_SCAN_RESULT = const(5)
AD_TYPE_NAME_COMPLETE = const(0x09)
AD_TYPE_NAME_SHORT = const(0x08)

ble = bluetooth.BLE()
device_set = set()

def _parse_name(adv_data):
    i = 0
    while i < len(adv_data):
        ad_len = adv_data[i]
        if ad_len == 0:
            break
        ad_type = adv_data[i+1]
        payload = adv_data[i+2:i+1+ad_len]
        if ad_type in (AD_TYPE_NAME_COMPLETE, AD_TYPE_NAME_SHORT):
            try:
                return payload.decode("utf-8").strip()
            except:
                return "Unreadable"
        i += ad_len + 1
    return "NoName"

def _scan_callback(event, data):
    global device_set
    if event == _IRQ_SCAN_RESULT:
        _, _, _, _, adv_data = data
        name = _parse_name(bytes(adv_data))
        device_set.add(name)

def scan_ble_devices():
    """
    阻塞扫描1秒,返回周围蓝牙设备名称列表(去重)
    return: list[str]
    """
    global device_set
    device_set.clear()
    # 初始化蓝牙与回调
    ble.active(True)
    ble.irq(_scan_callback)
    # 扫描时长1000ms,高速抓包参数
    ble.gap_scan(1000, 30000, 28000, 1)
    # 阻塞等待最多1秒
    t_start = time.ticks_ms()
    while time.ticks_diff(time.ticks_ms(), t_start) < 1000:
        pass
    # 停止扫描、关闭蓝牙
    ble.gap_scan(None)
    ble.active(False)
    # 转列表返回
    return list(device_set)


# 调用示例
if __name__ == "__main__":
    names = scan_ble_devices()
    print("扫描到蓝牙设备:")
    for n in names:
        print("-", n)

#返回
#names = scan_ble_devices()

后台版本

开启服务 start_background_full_scan()然后返回扫描的设备列表 scan_ble_devices()

from micropython import const
import bluetooth
import time

# BLE常量
_IRQ_SCAN_RESULT = const(5)
AD_TYPE_NAME_COMPLETE = const(0x09)
AD_TYPE_NAME_SHORT = const(0x08)

ble = bluetooth.BLE()
# 存储结构 {设备名称: 最后收到广播时间戳}
dev_cache = dict()
SCAN_RUNNING = False
# 设备超时时间:1500ms未收到广播判定离开
DEV_TIMEOUT = 1500

def _parse_name(adv_data):
    """解析广播包中的蓝牙设备名称"""
    idx = 0
    data_len = len(adv_data)
    while idx < data_len:
        ad_len = adv_data[idx]
        if ad_len == 0:
            break
        ad_type = adv_data[idx + 1]
        payload = adv_data[idx + 2 : idx + 1 + ad_len]
        if ad_type in (AD_TYPE_NAME_COMPLETE, AD_TYPE_NAME_SHORT):
            try:
                return payload.decode("utf-8").strip()
            except Exception:
                return "Unreadable_Device"
        idx += ad_len + 1
    return "No_Name"

def ble_irq_callback(event, data):
    global dev_cache
    if event == _IRQ_SCAN_RESULT:
        _, _, _, _, adv_data = data
        dev_name = _parse_name(bytes(adv_data))
        now = time.ticks_ms()
        # 更新该设备最后活跃时间
        dev_cache[dev_name] = now

def start_background_full_scan():
    """启动后台永久高速扫描,匹配100ms间隔信标"""
    global SCAN_RUNNING
    if SCAN_RUNNING:
        return
    ble.active(True)
    ble.irq(ble_irq_callback)
    # gap_scan(永久扫描0, 扫描周期100000us=100ms, 监听窗口90000us=90ms, 主动扫描1)
    ble.gap_scan(0, 100000, 90000, 1)
    SCAN_RUNNING = True
    print("后台BLE扫描已启动 | 适配100ms间隔蓝牙信标")

def scan_ble_devices():
    """
    对外调用接口:瞬间返回当前在场蓝牙设备名称列表
    自动清理超时消失的设备,无阻塞
    return: list[str]
    """
    global dev_cache
    if not SCAN_RUNNING:
        return []
    now = time.ticks_ms()
    # 过滤超时设备
    valid_devs = []
    for name, last_tick in dev_cache.items():
        if time.ticks_diff(now, last_tick) < DEV_TIMEOUT:
            valid_devs.append(name)
    return valid_devs

def stop_ble_scan():
    """停止扫描、关闭蓝牙,程序结束时调用"""
    global SCAN_RUNNING
    if not SCAN_RUNNING:
        return
    ble.gap_scan(None)
    ble.active(False)
    SCAN_RUNNING = False
    dev_cache.clear()
    print("BLE扫描已关闭")

------------------- 测试示例 -------------------
if __name__ == "__main__":
    # 程序启动只执行一次,开启后台扫描
    start_background_full_scan()

    # 循环读取设备列表,随便高频调用无压力
    while True:
        dev_list = scan_ble_devices()
        print("==== 在场蓝牙设备 ====")
        if len(dev_list) == 0:
            print("无设备")
        else:
            for name in dev_list:
                print("-", name)
#         time.sleep_ms(500)

发表在 None | 留下评论

技术表

  • 蓝牙 广播(自定义31B)+扫描
  • lora 传输(大文件传输大于255B、中继)
  • E-INK显示
  • 按键IRQ
  • 温湿度传感器
  • 气体传感器
  • 24GHZ雷达检测人员
  • 录音模块
  • TTS模块(TTL)
发表在 None | 留下评论

thinkphp 分页 service化 终章

前端:

    <div class="row text-center">
        <div class="pagination">{$info->render()|raw}</div>
        <p class="text-muted">---共计<b class="text-danger">{$info->total()}</b>条---</p>
    </div>

控制器

    public function list(Pt12345Service $Pt12345Service){
        $status=$this->request->get('status/d',0);
        $info=['status'=>$status];
        $list=$Pt12345Service->search($info);

        #查询结果中将查询条件打包带入
                 $list->appends($this->request->param());

        $department=$Pt12345Service->department();
        $this->assign(['info'=>$list,"department"=>$department]);
        return $this->fetch();
    }

service

    public function search($info){
        $where=[];
        if(isset($info['status'])){
            if($info['status']<10){
                $where[]=['status','=',$info['status']];
            }
        }
        $list = Pt12345Model::where($where)
            ->order('id desc')
            ->paginate(20, false, ['query' => $info]);
#使用paginate分页,代替select,并且false,之后query内容未传入的$info变量
        return $list;
    }
发表在 None | 留下评论

Win7 selenium 自动化chrome

Python3.8.10
selenium可以pip安装

chrome 109最后一个win7 版本,chromedriver 同样需要注意版本(存放位置后使用Service设置路径 )

编写时候注意:

find_elements()返回对象列表,find_element()返回对象!

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service

service = Service(r"c:\chromedriver.exe")
driver = webdriver.Chrome(service=service)
driver.get("http://baidu.com/#/login")


#CLASS 选择器
driver.find_element(By.CLASS_NAME,"el-icon-arrow-up").click()
swap_c_s=driver.find_elements(By.CLASS_NAME, "el-select-dropdown__item")
time.sleep(3)
swap_c_s[7].click()
swap_c_v=driver.find_elements(By.CLASS_NAME,"el-input__inner")


#设置信息使用send_keys("")
swap_c_v[1].send_keys("admin")
swap_c_v[2].send_keys("admin")
driver.find_element(By.CLASS_NAME,"el-button--primary").click()

#Xpath选择器
driver.find_element(By.XPATH,'//span[contains(text(),"办理系统")]').click()
time.sleep(3)

打开页面必须 time.sleep 等待页面加载【JS动态页面必须注意AJAX回传也需要等待加载】

swap_tr_ones=driver.find_elements(By.XPATH,'(//table[contains(@class,"el-table__body")])[1]//tr')
for i in swap_tr_ones:
    tid=i.find_element(By.XPATH,'.//td[4]').text
    end=i.find_element(By.XPATH,'.//td[8]').text

#选择页面打开后 可以text 获取信息

打开新页面,需要使用driver.switch_to.window(driver.window_handles[1])
切换到之前的需要driver.switch_to.window(driver.window_handles[0]),上面二者后需要重新使用选择器抓取 ,关闭页面必须使用driver.close()

swap_click_ones=driver.find_elements(By.XPATH,'//table[contains(@class,"el-table__body")]//tr/td[4][not(contains(@class,"hidden"))]')
vx=[]
for i in swap_click_ones:
    i.click()
    time.sleep(3)
    driver.switch_to.window(driver.window_handles[1])
    driver.find_element(By.XPATH,'//span[contains(text(),"查看")]').click()
    #切换到新页面  老的页面 driver.switch_to.window(driver.window_handles[0])
    tid=driver.find_element(By.XPATH,'//label[contains(text(),"abc:")]/following-sibling::div/div').text
    phone=driver.find_element(By.XPATH,'//label[contains(text(),"电话")]/following-sibling::div').text
    info=driver.find_element(By.XPATH,'//label[contains(text(),"内容")]/following-sibling::div').text

    one={
        'tid':tid,
        'phone':phone,
        'info':info,
    }
    vx.append(one)
    driver.close()
    driver.switch_to.window(driver.window_handles[0])

图片保存图片

driver.save_screenshot("full_page.png")

元素保存图片

# 定位图片元素
img = driver.find_element(By.XPATH, "//img")
# 全屏截图临时文件
driver.save_screenshot("tmp.png")
full_img = Image.open("tmp.png")

# 获取元素位置尺寸
x = img.location['x']
y = img.location['y']
w = img.size['width']
h = img.size['height']

# 裁剪并保存
crop = full_img.crop((x, y, x + w, y + h))
crop.save("target_img.png")# 定位图片元素
img = driver.find_element(By.XPATH, "//img")
# 全屏截图临时文件
driver.save_screenshot("tmp.png")
full_img = Image.open("tmp.png")

# 获取元素位置尺寸
x = img.location['x']
y = img.location['y']
w = img.size['width']
h = img.size['height']

# 裁剪并保存
crop = full_img.crop((x, y, x + w, y + h))
crop.save("target_img.png")

Download 下载内容

https://www.python.org/downloads/release/python-3810

https://edgedl.me.gvt1.com/edgedl/release2/chrome/acihtkcueyye3ymoj2afvv7ulzxa_109.0.5414.120/109.0.5414.120_chrome_installer.exe

https://registry.npmmirror.com/binary.html?path=chromedriver/109.0.5414.74

发表在 None | 留下评论