MoreRSS

site iconelmagnifico | 云浅雪

程序员,架构师,无人机集群表演设计师,嵌入式工程师,maya插件开发者,多智能体研究者,独立游戏爱好者。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

elmagnifico | 云浅雪的 RSS 预览

Python gRPC

2024-11-22 00:00:00

Foreword

gRPC

example测试

安装gRPC库

pip install grpcio

安装gRPC工具

pip install grpcio-tools

下载官方例程

git clone -b v1.66.0 --depth 1 --shallow-submodules https://github.com/grpc/grpc

演示用例在这里

grpc/examples/python/helloworld

先启动服务端

python greeter_server.py

image-20241120171002516

可以看到已经在监听了

再启动客户端

python greeter_client.py

正常连接到了服务端

image-20241120171021552

源码分析

服务端

from concurrent import futures
import logging

import grpc
import helloworld_pb2
import helloworld_pb2_grpc

# 继承自helloworld_pb2_grpc.GreeterServicer,重写了sayhello的函数
class Greeter(helloworld_pb2_grpc.GreeterServicer):
    def SayHello(self, request, context):
        # 对应返回 hello 和访问者的名字
        return helloworld_pb2.HelloReply(message="Hello, %s!" % request.name)


def serve():
    # 启动还是比较简单的,设置好端口
    port = "50051"
    # 调用helloworld_pb2_grpc就完成了
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
    server.add_insecure_port("[::]:" + port)
    server.start()
    print("Server started, listening on " + port)
    server.wait_for_termination()


if __name__ == "__main__":
    logging.basicConfig()
    serve()

客户端

from __future__ import print_function

import logging

import grpc
import helloworld_pb2
import helloworld_pb2_grpc


def run():
    # NOTE(gRPC Python Team): .close() is possible on a channel and should be
    # used in circumstances in which the with statement does not fit the needs
    # of the code.
    print("Will try to greet world ...")
    # 设置本地 端口
    with grpc.insecure_channel("localhost:50051") as channel:
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        # 发送信息 并等待结果
        response = stub.SayHello(helloworld_pb2.HelloRequest(name="you"))
    print("Greeter client received: " + response.message)


if __name__ == "__main__":
    logging.basicConfig()
    run()

helloworld_pb2_grpc.py

# 继承的原型函数在这里
class GreeterServicer(object):
    """The greeting service definition.
    """

    def SayHello(self, request, context):
        """Sends a greeting
        """
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')

    def SayHelloStreamReply(self, request, context):
        """Missing associated documentation comment in .proto file."""
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')

    def SayHelloBidiStream(self, request_iterator, context):
        """Missing associated documentation comment in .proto file."""
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')
        
# 主要是这个函数,把函数的返回绑定到一起
def add_GreeterServicer_to_server(servicer, server):
    rpc_method_handlers = {
            'SayHello': grpc.unary_unary_rpc_method_handler(
                    servicer.SayHello,
                    request_deserializer=helloworld__pb2.HelloRequest.FromString,
                    response_serializer=helloworld__pb2.HelloReply.SerializeToString,
            ),
            'SayHelloStreamReply': grpc.unary_stream_rpc_method_handler(
                    servicer.SayHelloStreamReply,
                    request_deserializer=helloworld__pb2.HelloRequest.FromString,
                    response_serializer=helloworld__pb2.HelloReply.SerializeToString,
            ),
            'SayHelloBidiStream': grpc.stream_stream_rpc_method_handler(
                    servicer.SayHelloBidiStream,
                    request_deserializer=helloworld__pb2.HelloRequest.FromString,
                    response_serializer=helloworld__pb2.HelloReply.SerializeToString,
            ),
    }
    # 创建服务名称和通用句柄
    generic_handler = grpc.method_handlers_generic_handler(
            'helloworld.Greeter', rpc_method_handlers)
    # server添加通用的句柄
    server.add_generic_rpc_handlers((generic_handler,))
    # 将处理方法注册给server
    server.add_registered_method_handlers('helloworld.Greeter', rpc_method_handlers)

helloworld_pb2.py

"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
    _runtime_version.Domain.PUBLIC,
    5,
    27,
    2,
    '',
    'helloworld.proto'
)
# @@protoc_insertion_point(imports)

_sym_db = _symbol_database.Default()



# 这里直接用代码的形式写了一个helloworld的描述符
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10helloworld.proto\x12\nhelloworld\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x1d\n\nHelloReply\x12\x0f\n\x07message\x18\x01 \x01(\t2\xe4\x01\n\x07Greeter\x12>\n\x08SayHello\x12\x18.helloworld.HelloRequest\x1a\x16.helloworld.HelloReply\"\x00\x12K\n\x13SayHelloStreamReply\x12\x18.helloworld.HelloRequest\x1a\x16.helloworld.HelloReply\"\x00\x30\x01\x12L\n\x12SayHelloBidiStream\x12\x18.helloworld.HelloRequest\x1a\x16.helloworld.HelloReply\"\x00(\x01\x30\x01\x42\x36\n\x1bio.grpc.examples.helloworldB\x0fHelloWorldProtoP\x01\xa2\x02\x03HLWb\x06proto3')

_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'helloworld_pb2', _globals)
if not _descriptor._USE_C_DESCRIPTORS:
  _globals['DESCRIPTOR']._loaded_options = None
  _globals['DESCRIPTOR']._serialized_options = b'\n\033io.grpc.examples.helloworldB\017HelloWorldProtoP\001\242\002\003HLW'
  _globals['_HELLOREQUEST']._serialized_start=32
  _globals['_HELLOREQUEST']._serialized_end=60
  _globals['_HELLOREPLY']._serialized_start=62
  _globals['_HELLOREPLY']._serialized_end=91
  _globals['_GREETER']._serialized_start=94
  _globals['_GREETER']._serialized_end=322
# @@protoc_insertion_point(module_scope)

实际这里使用的proto文件,是如下定义的

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

自定义函数

实际使用的proto文件是在examples/protos/helloworld.proto中的,这里添加一个新的函数

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}

  rpc SayHello2 (HelloRequest) returns (HelloReply) {}

  rpc SayHelloStreamReply (HelloRequest) returns (stream HelloReply) {}

  rpc SayHelloBidiStream (stream HelloRequest) returns (stream HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

需要重新生成对应的代码

python -m grpc_tools.protoc -I../../protos --python_out=. --pyi_out=. --grpc_python_out=. ../../protos/helloworld.proto

这里就会重新生成了

def add_GreeterServicer_to_server(servicer, server):
    rpc_method_handlers = {
            'SayHello': grpc.unary_unary_rpc_method_handler(
                    servicer.SayHello,
                    request_deserializer=helloworld__pb2.HelloRequest.FromString,
                    response_serializer=helloworld__pb2.HelloReply.SerializeToString,
            ),
            'SayHello2': grpc.unary_unary_rpc_method_handler(
                    servicer.SayHello2,
                    request_deserializer=helloworld__pb2.HelloRequest.FromString,
                    response_serializer=helloworld__pb2.HelloReply.SerializeToString,
            ),

server和client都增加hello2以后,再次运行就能看到已经给过来正确的反应了

image-20241122144701070

到这里最简单的gRPC就完成了

小总结

核心就三步:

  1. 定义proto,其实就是定义函数和参数,
  2. 生成,生成会自动根据定义,生成中间需要的类或者成员函数
  3. 修改server和client的调用

gRPC的流式传输

上面演示的例子都是C/S架构的,也是gRPC常用的模式,一方请求,一方应答,这是普通的RPC。服务方是不能主动发起请求的,只有客户方主动。还有其他3种方式。

  • 响应流式传输
  • 请求流式传输
  • 双向流式传输

还是之前的例子中,有对应的流式实现

NUMBER_OF_REPLY = 10
class Greeter(MultiGreeterServicer):
    async def sayHello(
        self, request: HelloRequest, context: grpc.aio.ServicerContext
    ) -> HelloReply:
        logging.info("Serving sayHello request %s", request)
        for i in range(NUMBER_OF_REPLY):
            yield HelloReply(message=f"Hello number {i}, {request.name}!")

对于服务端的响应,这里可以看到返回了10此请求,并且这个函数是异步的

async def run() -> None:
    async with grpc.aio.insecure_channel("localhost:50051") as channel:
        stub = hellostreamingworld_pb2_grpc.MultiGreeterStub(channel)

        # Read from an async generator
        async for response in stub.sayHello(
            hellostreamingworld_pb2.HelloRequest(name="you")
        ):
            print(
                "Greeter client received from async generator: "
                + response.message
            )

        # Direct read from the stub
        hello_stream = stub.sayHello(
            hellostreamingworld_pb2.HelloRequest(name="you")
        )
        while True:
            response = await hello_stream.read()
            if response == grpc.aio.EOF:
                break
            print(
                "Greeter client received from direct read: " + response.message
            )

客户端这边,前面是异步流式获取,后面是正常流式获取获取

可能看例子,这里流式传输的意义不是很明显,除了能多次发送请求或者多次响应,还有啥用。

  • 大文件流式传输就需要多次请求和多次响应,比如音频、视频
  • 主动推送或者回报就需要流式来实现,比如广告、广播推送
  • 高并发,可以同时响应多个请求,不再是顺序执行,串联影响效率
  • 任务完成的进度回显就必须多次响应

grpc的双向流式可以类比成WebSocket,客户端和服务器都可以互相发送信息

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}

  rpc SayHello2 (HelloRequest) returns (HelloReply) {}

  rpc SayHelloStreamReply (HelloRequest) returns (stream HelloReply) {}

  rpc SayHelloBidiStream (stream HelloRequest) returns (stream HelloReply) {}
}

在流式传输的例子中proto文件的定义使用了一个特殊关键词,stream凡是被stream修饰的参数,那么传输时就会采用流式。

如果修饰到返回值,那就是服务器流式,如果修饰参数,那就是客户端流式,如果同时有那就是双向流式传输

Summary

总体来说gRPC就是这样,流式上感觉似乎没有WebSocket简单,特别是如果是用来做双向交互的时候WebSocket似乎更简单,更好做一些

Quote

https://grpc.io/docs/languages/python/quickstart/

https://blog.yuanpei.me/posts/grpc-streaming-transmission-minimalist-guide/

https://hamhire.tech/posts/coding/grpc-03.stream-demo.html

STM32 MCU移植SSH

2024-11-15 00:00:00

Foreword

给MCU移植Crypto、SSL、SSH、SFTP等库,真的找不到一个例子,目前看到的库大部分都是商用的。

比如wolfssh、CycloneSSH、libssh2、TinySSH、microSSH、Dropbear,这些库可能linux使用比较多,但是那边安装移植也方便多了,降到MCU一库难求,更别说详细的移植文档了,基本没有

wolfssh

https://www.wolfssl.com/products/wolfssh/

wolfssh移植

wolfssh的库整个集成到了CubeMX中,简单的几个操作就可以把ssh集成进去

image-20241114174448563

系统时钟不要用systick,留给FreeRTOS用

image-20241114174514938

随便加一个input IO作为SD卡的输入检测

image-20241114185644689

再随便加一个串口作为SSH的端口

image-20241114174543271

以太网选择RMII模式,地址改到0x24开头

image-20241114174623401

SD卡改到4线模式

image-20241114174651486

FATFS选择SD卡

image-20241114194722092

打开RNG,SSL需要随机数生成

image-20241114174713351

LWIP开启,Driver_PHY选择LAN8742,后面也是

image-20241114174814132

接着wolfSSL和wolfSSH都需要下载,开启

image-20241114174845054

SSH开启以后,IO选择LWIP,SFTP打开,SCP可以关闭

image-20241114195103602

SSL这里需要支持FreeRTOS

image-20241114195129997

关闭wolfCrypt test,这个编译会带进来很多多语言的内容,keil编译会出错

剩下设置就随便改改,就可以生成代码了,生成以后依然编译不了,还需要修改一些点

image-20241114185407202

首先在wolfSSL.I-CUBE-wolfSSL_conf.h里需要定义板子,如果不是wolf测试的板子,需要在下面这里定义具体用的是哪种类型的,具体硬件库啊、加密方式啊、库的版本、使用的串口是哪个需要明确一下

    //#warning Please define a hardware platform!
    /* This means there is not a pre-defined platform for your board/CPU */
    /* You need to define a CPU type, HW crypto and debug UART */
    /* CPU Type: WOLFSSL_STM32F1, WOLFSSL_STM32F2, WOLFSSL_STM32F4,
        WOLFSSL_STM32F7, WOLFSSL_STM32H7, WOLFSSL_STM32L4, WOLFSSL_STM32L5,
        WOLFSSL_STM32G0, WOLFSSL_STM32WB and WOLFSSL_STM32U5 */
    #define WOLFSSL_STM32H7

    /* Debug UART used for printf */
    /* The UART interface number varies for each board/CPU */
    /* Typically this is the UART attached to the ST-Link USB CDC UART port */
    #define HAL_CONSOLE_UART huart1

    /* Hardware Crypto - uncomment as available on hardware */
    //#define WOLFSSL_STM32_PKA
    //#define NO_STM32_RNG
    //#undef  NO_STM32_HASH
    //#undef  NO_STM32_CRYPTO
    //#define WOLFSSL_GENSEED_FORTEST /* if no HW RNG is available use test seed */
    #define STM32_HAL_V2
  • H743是没有HASH,也没有CRYPTO的,但是有RNG

移除所有test相关代码,这个部分用gcc编译更容易过一些

..\include\stdio.h(372): error:  #2746: argument for attribute "nonnull" is larger than number of parameters
                      const char * __restrict /*format*/, ...) __attribute__((__nonnull__(1,2)));

这个错暂时解不掉,得用CubeIDE编译才行,Keil这里过不去

CycloneSSH

https://www.oryx-embedded.com/products/CycloneSSH.html

CycloneSSH是oryx-embedded下的一个库,他们包含很多相关组件

image-20241114175150373

https://www.st.com.cn/zh/partner-products-and-services/cyclonessh.html

ST官方也有他们的合作页面,但是CubeMX里不支持生成

虽然Github也开源了,放出来了源码,但是那个源码一个demo都没有,任何文档也没有,接口也没说,只能干看着。

image-20241114175036051

要查看Demo需要下载官方的代码,然后在demo里面有各个公司各个板子的demo工厂,这里以stm32h743i_eval的板子为例,测试了一下SFTP的demo

先修改代码同意协议,否则无法生成

/*
 * CycloneTCP Open is licensed under GPL version 2. In particular:
 *
 * - If you link your program to CycloneTCP Open, the result is a derivative
 *   work that can only be distributed under the same GPL license terms.
 *
 * - If additions or changes to CycloneTCP Open are made, the result is a
 *   derivative work that can only be distributed under the same license terms.
 *
 * - The GPL license requires that you make the source code available to
 *   whoever you make the binary available to.
 *
 * - If you sell or distribute a hardware product that runs CycloneTCP Open,
 *   the GPL license requires you to provide public and full access to all
 *   source code on a nondiscriminatory basis.
 *
 * If you fully understand and accept the terms of the GPL license, then edit
 * the os_port_config.h header and add the following directive:
 *
 * #define GPL_LICENSE_TERMS_ACCEPTED
 */

#ifndef GPL_LICENSE_TERMS_ACCEPTED
   #error Before compiling CycloneTCP Open, you must accept the terms of the GPL license
#endif

如果不定义GPL_LICENSE_TERMS_ACCEPTED 会导致这里报错,编译不下去

image-20241114160904024

image-20241114160849834

CycloneSSH的问题在于虽然他用demo可以直接编译,但是他底层调用的是他自己的TCPIP接口,也就是CycloneTCP,而不是Lwip,这就导致如果你要移植,必须还得搞懂TCP这里的接口具体是什么的,怎么往下对接,Lwip的底层你也得熟悉才能完成这个事情

Summary

带了安全的协议真的麻烦

Quote

https://www.stmcu.org.cn/module/forum/forum.php?mod=viewthread&tid=616445

ChatGPT-Mirror部署和体验

2024-11-15 00:00:00

Foreword

cws一直有问题,而且只支持access token,经常要换很麻烦。之前有关注到dairoot的mirror,这次刚好试一下,发现体验还行

ChatGPT-Mirror

https://github.com/dairoot/ChatGPT-Mirror

项目很简单

https://chatgpt.dairoot.cn/

官方体验站,也可以使用免费账号测试,基本都差不多

部署

脚本内是docker,所以机器需要提前安好docker

git clone https://github.com/dairoot/ChatGPT-Mirror.git

cd ChatGPT-Mirror/

# 修改管理后台账号密码
cp .env.example .env && vi .env


chmod +x ./deploy.sh
# 可能的话需要改一下 把启动脚本的docker compose改成docker-compose
vi ./deploy.sh
# 启动
./deploy.sh

caddy 反代一下

gpt.你的域名.com {
    reverse_proxy 127.0.0.1:50002
}

测试

http://你的ip:50002

可以正常访问了

image-20241115151616816

管理员账号先进去添加使用者账号和ChatGPT账号

image-20241115151723399

  • 支持独立会话,支持指定号池,也能单独冻结

账号支持使用session token,可以维持30天,还是比较好的

image-20241115151759671

可以把一些账号绑定给某一个号池,这样方便管理

image-20241115151930401

访问起来和GPT差不多,还可以

image-20241115152054561

Summary

团队内部使用足够了,后续把套餐再升级到team那就更方便了

类似的镜像站还有始皇的,之前看好像也可以通过权限分享

new.oaifree.com

Quote

https://dairoot.cn/2024/07/02/install-chatgpt-mirror/

GitLab联动Jenkins

2024-11-12 00:00:00

Foreword

GitLab联动Jenkins完成CI\CD流程

webhooks方式

Jenkins

Jenkins升级

Jenkins自动升级,完成以后Jenkins就无法启动了,查log可以看到提示说明java版本太老了

Jenkins升级后不再支持java11的,需要安装java17或者21,我这里直接选择21

image-20241111172051911

进入Jenkins目录下,修改jenkins.xml文件,将其中的jdk修改为新安装的jdk,然后重启一点电脑,重启jenkins服务即可

GitLab插件

首先Jenkins需要安装GitLab插件,否则收不到GitLab的请求

image-20241111171933060

然后在Credentials中添加GitLab的账号

image-20241111175616864

用户名和密码的形式即可

  • 这个操作只要做一次即可

image-20241111175641965

GitLab API token

还有一种方式创建GitLab API token,这种是有使用年限的

image-20241111183756934

系统设置中的GitLab,然后添加Credentials,选择GitLab API token

image-20241111183824235

这个token可以从GitLab用户设置中创建

image-20241111183919335

  • 注意token有效期只有1年

然后把token复制到jenkins那边,通过右侧的Test Connection就能测试出来token是否可以使用

image-20241111184053987

正常的话就显示Success

这种方式创建的token,在配置CI的时候,可以不选择credentials,Jenkins会自动帮你匹配对应的GitLab库的token

测试配置

创建一个空配置,前面的GitLab相关选项都不选择,直接选Git 仓库中填入对应地址,选择刚才创建的GitLab账号

image-20241111175748241

分支选好

image-20241111180004280

选择当推送时进行build,记录下旁边写的webhook的url,有些时候端口可以不要(如果端口是对外使用的),展开下面的高级选项

image-20241111180030191

点击生成token,记录下这个token值,剩下执行脚本或者命令部分就正常填写,没有大差别了,弄好以后保存

GitLab

回到GitLab,由于是内网机器,所以需要开启内网连接的请求

管理员权限,进入设置-网络管理

image-20241111180351796

出站请求中允许本地网络,必要的话可以把本地的域名或者是ip直接给进去,防止被拦截

  • 这个操作只需要做一次即可

image-20241111180528806

回到工程中,设置-Webhooks,新建一个,URL使用刚才记录的URL,Secret令牌使用刚才的token,保存

image-20241111180640920

测试,点击推送事件

image-20241111180631205

可以看到正常返回200

image-20241111180720767

Jenkins侧也有正确的响应

到这里整个webhook的触发方式就可以正常使用了

集成方式

按道理说GitLab和Jenkins还有另外一个集成的地方,但是前面几次测试都提示缺少Token,导致并不能触发。

按理来说集成方式应该是最简单的,这里重新参考了官方的配置指南,并且重试了一遍流程以后发现可以走通

Webhook中的GitLab API token的配置操作需要提前做完,然后才能接着操作

image-20241111192137585

Jenkins中新建一个配置,这次选择GitLab连接和仓库名称,千问不要写错了

image-20241111192228739

下面的配置里勾上tag和merge触发,其实不勾也行。

注意高级中,一定要确保下面的secret token是空白的,如果不是使用clear清空他

image-20241111192327327

image-20241111192346776

最后构建选择Publish build status to GitLab,就可以把状态返回回来了

image-20241111192605510

回到GitLab的仓库中,在集成-Jenkins中启用集成,并且输入Jenkins的URL,然后输入Jenkins的配置名称

  • 注意是Jenkins的配置名称,不是项目名称

image-20241111192705639

接着输入Jenkins的账号和密码,保存,测试设置,就能看到正常工作了

Summary

还得是官方文档,全网大部分都是一通乱操作,最后都走到了webhooks的方式中去了,而集成是最简单的

Quote

https://blog.csdn.net/weixin_63294004/article/details/143671722

https://blog.csdn.net/weixin_43546282/article/details/129130533

https://www.cnblogs.com/ygbh/p/17483811.html#_label3_1_3_2

https://docs.GitLab.com/ee/integration/jenkins.html#grant-jenkins-access-to-the-GitLab-project

遥控和香氛按钮接入米家失败

2024-11-11 00:00:00

Foreword

记录一下接入米家失败的几个案例

遥控器接入米家

image-20241111000006292

遥控倒是挺好拆的,经过测试接入按钮是物理的,成功的概率应该很大了

接下来就翻车了,反复测试了好几次发现,直接把按钮接入地或者电源,都不能正常触发按键,被控对象有反应,但是不能正常工作。

仔细查了一下主控芯片:TLSR8366ET24

image-20241111000909562

它实际可以当作GPIO的引脚其实不多,但是结合遥控器,要控的除了13个按键,其实还有5个led灯,不可能接18个GPIO去做独立按键控制的,所以他其实这里用了矩阵键盘,逆向了电路以后,发现他的按键两端IO确实都是接入了主控芯片,而不是地

实际遇到的情况:

image-20241110232059813

由于使用了矩阵键盘,这里又想保留原本的键盘、又想接入米家就无法做到了,除非米家模块支持Bypass模式,在输入的时候可以进行闭合/断开操作,平常保持断开/闭合,让扫描信号可以正常工作,从而可以模拟按键,否则无论怎么设计都没办法直接接入到米家中

所以遇到这种情况就没办法接入了,不过测试了一下米家模块,用2节7号电池,活不过一周就耗尽了,这个还是得配合电源一起使用。

迪富电子

https://www.denvel.com

顺手查了一下遥控的生产厂家,迪富电子,专门做遥控的

香氛机接入米家

image-20241111001712550

别问为什么不买接入米家的香氛机,就是爱折腾(眼瞎买错了,而且还是两次)

image-20241111002007373

这个按键是电容的,所以用物理的方式肯定无法直接触发,而且这个还要做防水,实际无损拆开也不容易。

image-20241111002110444

经过一番搜索 ,还是看到了可以控制电容屏的触摸按钮,但是店家也不能保证可以控制这种类型的按钮,只能说买来试试,不行就88会员免费退了

image-20241111002349528

设计的还是挺简单的,看起来是通过PWM引起电容变化,进而被检测的,用的耳机接头,一个小板子可以控4个,支持typec供电,也给预留了电池供电口

image-20241111002324729

经过测试,果然不行。如果用手拿着电容头,确实可以触发,但是如果按键触发电容头变化,就不行。

店家也挺好的,让我直接整个给他退回去,他再研究一下,看能否处理。经过他测试,试用了他们新的高频电容头,发现不行,只好给我退回来了。

这个电容触发模块本身的价格+香氛机比直接买接入米家的香氛还贵,成品还是更香啊

智能冲水改造

垃圾马桶盖太烂了,智能太差了,既不能接入米家,也不能自动冲水,妥妥的废物。

之前看了一些智能冲水的外部产品,虽然不能进米家但是也不错

image-20241111002925404

image-20241111003157048

触发逻辑很简单,被遮挡6s以上,释放时会自动进行冲水,同时还有一个触摸按键可以直接触发冲水

image-20241111002903499

安装需要注意排水阀直径需要在60-80mm直接,否则这个环套不上去,同时水箱盖按钮长度有36mm,否则可能盖不上。

安装完以后,放好感应器基本完美了。

理想马桶盖+冲水的智能逻辑:

小的,检测到来人自动开盖(顶盖+座圈),离开后自动关盖,离开后6s自动冲水,触发香氛

大的,检测到来人自动开盖(顶盖),冲洗,冲洗完成后,自动关盖,离开后6s自动冲水,触发香氛

现实马桶盖+冲水的智能逻辑:

小的,检测到来人自动开盖(顶盖),手动按遥控器开座圈,离开后6s自动冲水

大的,检测到来人自动开盖(顶盖),冲洗,冲洗完成后,手动按遥控关盖,离开后6s自动冲水

  • 由于是侧面检测,实际离开判定不是很准确,很容易在还没离开的情况下触发冲水

正常感应应该是安装在水箱上的,但是这个马桶盖很容易自动开合,那就会出现自动冲水,为了减少误触只好安装在侧面

压力传感器

image-20241111005424604

这个不要买就行了,触发灵敏度不能调节,触发条件其实还是有点苛刻的,要求压感片被很明显的压变形才能触发,只有坐姿比较容易,其他方式,比如躺卧等比较难触发。

这个是拿电阻式薄膜压力带+门窗触发器改造来的,实际挺简单的

image-20241111005646344

咸鱼上还有一种压力片的感应点更多一些,这种稍微好一些。

YF-DR-CM-V2

image-20241111004506656

image-20241113150312432

看了一下主要触发的方式,EL357N是一个光电耦合器模块,理论上是用来隔离输入和输出的,但是这里直接把输出给到了这个软的吸盘,而且这个吸盘竟然还导电

image-20241113150337729

主控是STC的8G1K08A,简单一个单片机,主要用来输出PWM给光电模块

给的PWM脉冲大概是3Hz,占空比是25%,然后就能触发屏幕的点击了

其他的方式触发点击,本质上是一样的方案,所以没有尝试

其他

image-20241111005941319

还有纯机械的方式,直接模拟人手了,这种有点太弱智了,和拿个舵机直接控没啥区别了,不能接受

image-20241111010028986

电容按键一般后面都有芯片,其实可以看对应芯片是以什么方式输出结果的,如果I2C,类似之前升降桌的芯片,那种就很难从中间介入。但是如果输出结果也是简单的0/1信号,那也可以直接从芯片侧接入,来接入智能,同时还能保留原本的按键

Summary

还是得尽量选择天生带接入的,后期改造很难做到理想

Quote

https://item.taobao.com/item.htm?id=771817542208