2112|6

41

帖子

2

TA的资源

一粒金砂(中级)

楼主
 

【平头哥RVB2601创意应用开发】 RVB2601之WiFi联网获取天气信息方式介绍和使用 [复制链接]

 

项目往期连载文章传送门:
【平头哥RVB2601创意应用开发】 RVB2601之KV组件介绍和使用
【平头哥RVB2601创意应用开发】 RVB2601之OLED-SSD1306移植介绍和使用
【平头哥RVB2601创意应用开发】 RVB2601之cJSON介绍和使用

 

一、联网

开发板有W800的WiFi芯片,是与MCU是通过SPI连接通讯的。对于一个可以联网的设备,我们一定要好好使用起来它,那就去做个获取天气的小设备吧。

我的联网demo,很多是参考历程OTA的,那么一点点把做的事情说明白,希望其他网友也可以尝试用我的代码自己修改做出自己想要的功能。

1.网络初始化

研究demo,我们知道了,初始化网络需要调用的接口是”void network_init()”,这里面实现了对W800的驱动,以及通过网络组件netmgr进行的网络管理,在这个函数里,先创建了微服务netmgr,然后我们主要去实现,设置WiFi要连接的SSID和PASSWORD,并且实现对W800模块的注册和做好当WiFi拿到IP或者断开时候的回调函数

static void network_init()
{
    w800_wifi_param_t w800_param;
    /* init wifi driver and network */
    w800_param.reset_pin      = PA21;
    w800_param.baud           = 1*1000000;
    w800_param.cs_pin         = PA15;
    w800_param.wakeup_pin     = PA25;
    w800_param.int_pin        = PA22;
    w800_param.channel_id     = 0;
    w800_param.buffer_size    = 4*1024;

    wifi_w800_register(NULL, &w800_param);
    app_netmgr_hdl = netmgr_dev_wifi_init();

    if (app_netmgr_hdl) {
        utask_t *task = utask_new("netmgr", 2 * 1024, QUEUE_MSG_COUNT, AOS_DEFAULT_APP_PRI);
        netmgr_service_init(task);
        netmgr_config_wifi(app_netmgr_hdl, W800_SSID, sizeof(W800_SSID), W800_PASSWARD, sizeof(W800_PASSWARD));
        // w800_module_init(task,  &w800_param);
        netmgr_start(app_netmgr_hdl);
        /* Subscribe */
        event_subscribe(EVENT_NETMGR_GOT_IP, network_event, NULL);
        event_subscribe(EVENT_NETMGR_NET_DISCON, network_event, NULL);
    }
}
  1. 当WiFi连接上热点,获取到IP之后,就开始启动获取天气的线程:app_weather_start();

    static void network_event(uint32_t event_id, const void *param, void *context)
    {
        switch(event_id) {
        case EVENT_NETMGR_GOT_IP:
    		LOGD(TAG, "net got ip");
            app_weather_start();
            break;
        case EVENT_NETMGR_NET_DISCON:
            LOGD(TAG, "net disconnect");
            break;
        }
    
        /*do exception process */
        app_exception_event(event_id);
    }
    

3.在 void board_yoc_init(void) 里,对network,进行初始化void network_init()

这样第一步联网功能就完成了。当然记得库要加上w800的,在我们设置好ssid和password之后,给设备烧录程序,重启设备,就可以在日志里看到设备连接网络成功的情况以及获取到的IP了。

// 图

二、移植http的接口和network的接口

设备连上网络还不能去获取天气信息,需要有http get的请求接口,以及底层对http get之类API接口的实现。

这里参考了 OTA demo,设备通过http post 请求更新的固件。将里面http和network的接口做了一部分修改,放到了应用文件中,命名为:app_http.c 和 app_network.c 以及对应的头文件 http.h 和 network.h

这里直接贴代码吧,大家自己研究下,或者直接拿来拷贝就好了

app_http.c

/*
 * Copyright (C) 2019-2020 Alibaba Group Holding Limited
 */

#include <stdlib.h>

#include "http.h"
#include <aos/debug.h>
#include "network.h"

#define BUFFER_SIZE 2048
#define TAG "http"

http_t *http_init(const char *path)
{
    char *ip, *port_str;
    char *end_point;
    char *host = NULL;
    int port;

    // FIXME: only support HTTP now.
    if (path == NULL || !strstr(path, "http://")) {
        return NULL;
    }

    http_t *http = aos_zalloc_check(sizeof(http_t));

    http->url = strdup(path);
    if (http->url == NULL) {
        goto error;
    }

    host = http->url + sizeof("http://") - 1;
    if (strlen(host) == 0) {
        goto error;
    }
    http->port = 80;

    ip = http->url + strlen("http://");

    port_str = strrchr(host, ':');

    if (port_str != NULL) {
        *port_str = 0;
        port_str ++;
        port = strtol(port_str, &end_point, 10);
        http->port = port;
    } else {
        port = 80;
        end_point = strchr(ip, '/');
    }

    if (end_point != NULL) {
        http->path = strdup(end_point);
        LOGD(TAG, "http: path %s", http->path);

        if (http->path == NULL) {
            goto error;
        }
    } else {
        goto error;
    }

    http->buffer = (uint8_t *)aos_malloc(BUFFER_SIZE);
    if (http->buffer == NULL) {
        goto error;
    }
    memset(http->buffer, 0, BUFFER_SIZE);

    *end_point = 0;
    http->host = strdup(ip);
    if (http->host == NULL) {
        goto error;
    }

    LOGD(TAG, "http connect: %s:%d", http->host, port);

    network_init(&http->net);
    if (http->net.net_connect(&http->net, http->host, port, SOCK_STREAM) != 0) {
        goto error;
    }

    return http;

error:
    if (http) {
        if (http->url) aos_free(http->url);
        if (http->path) aos_free(http->path);
        if (http->host) aos_free(http->host);
        if (http->buffer) aos_free(http->buffer);
        aos_free(http);
    }

    return NULL;
}

int http_head_sets(http_t *http, const char *key, const char *value)
{
    int len;
    if (http->buffer == NULL) {
        return -1;
    }

    len = snprintf((char*)http->buffer + http->buffer_offset, BUFFER_SIZE - http->buffer_offset,
            "%s: %s\\r\\n", key, value);
    if (len <= 0) {
        return -1;
    }

    http->buffer_offset += len;

    return 0;
}

int http_head_seti(http_t *http, const char *key, int value)
{
    int len;
    if (http->buffer == NULL) {
        return -1;
    }

    len = snprintf((char*)http->buffer + http->buffer_offset, BUFFER_SIZE - http->buffer_offset,
            "%s: %d\\r\\n", key, value);
    if (len <= 0) {
        return -1;
    }

    http->buffer_offset += len;

    return 0;
}

int http_post(http_t *http, char *playload, int timeoutms)
{
    char *temp_buff;
    if ((temp_buff = strdup((char *)http->buffer)) == NULL) {
        return -1;
    }

    // temp_buff = strdup((char *)http->buffer);
    memset(http->buffer, 0, BUFFER_SIZE);

    snprintf((char *)http->buffer, BUFFER_SIZE, "POST %s HTTP/1.1\\r\\n", http->path);

    strcat((char *)http->buffer, temp_buff);

    strcat((char *)http->buffer, "\\r\\n");

    strcat((char *)http->buffer, playload);

    // LOGD(TAG, "http post sendbuffer: %s", http->buffer);

    aos_free(temp_buff);

    return http->net.net_write(&http->net, http->buffer, strlen((char*)http->buffer), timeoutms);
}

int http_get(http_t *http, int timeoutms)
{
    char *temp_buff;
    if ((temp_buff = strdup((char *)http->buffer)) == NULL) {
        return -1;
    }

    // temp_buff = strdup((char *)http->buffer);
    memset(http->buffer, 0, BUFFER_SIZE);

    snprintf((char *)http->buffer, BUFFER_SIZE, "GET %s HTTP/1.1\\r\\n", http->path);

    strcat((char *)http->buffer, temp_buff);

    strcat((char *)http->buffer, "\\r\\n");

    // LOGD(TAG, "http get sendbuffer: %s", http->buffer);

    aos_free(temp_buff);

    return http->net.net_write(&http->net, http->buffer, strlen((char*)http->buffer), timeoutms);
}

static int http_parse_head(http_t *http, char **head)
{
    char *content;
    char *endpoint;
    int content_len;
    char *code_str;
    int code;
    char *end_point;

    *head = strstr((char*)http->buffer, "\\r\\n\\r\\n");

    if (*head == NULL) {
        return 0;
    }

    *head += 4;

    // FIXME: server connection close, need reconnect
    if (strstr((char*)http->buffer, "Connection: close")) {
        return -2;
    }

    if ((content = strstr((char*)http->buffer, "Content-Length: ")) == NULL) {
        code_str = strstr((char *)http->buffer, "HTTP/1.1");

        code = atoi(code_str + strlen("HTTP/1.1"));
        if (code != 206 && code != 200) {
            LOGD(TAG, "http code :%d", code);
            return -1;
        }

        content_len = strtol(*head, &end_point, 16);

        if (end_point == NULL) {
            return 0;
        }
    } else {
        // content_len = atoi(content + strlen("Content-Length: "));
        content_len = strtol(content + strlen("Content-Length: "), &endpoint, 10);

        if (endpoint == NULL || *endpoint != '\\r') {
            return -1;
        }
    }

    // LOGD(TAG, "http parse head: %s %d", http->buffer, content_len);
    return content_len;
}

static int net_quick_reconnect(http_t *http)
{
    LOGW(TAG, "i need reconnect http");
    http->net.net_disconncet(&http->net);
    if (http->net.net_connect(&http->net, http->host, http->port, SOCK_STREAM) != 0) {
        LOGE(TAG, "reconnect failed!");
        return -1;
    }
    return 0;
}

static int net_read(int fd, unsigned char *buffer, int len, int timeout_ms)
{
    struct timeval interval = {timeout_ms / 1000, (timeout_ms % 1000) * 1000};

    if (interval.tv_sec < 0 || (interval.tv_sec == 0 && interval.tv_usec <= 100)) {
        interval.tv_sec = 0;
        interval.tv_usec = 10000;
    }

    if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval,
                   sizeof(struct timeval))) {
        return -1;
    }

    int rc = recv(fd, buffer, len, 0);

    return rc;
}

int http_wait_resp(http_t *http, char **head_end, int timeoutms)
{
    int recv_len = 0;
    int len;
    int content_len;

again:
    recv_len = 0;
    *head_end = NULL;
    content_len = 0;
    memset(http->buffer, 0, BUFFER_SIZE);

    while(recv_len < BUFFER_SIZE) {
        int pre_read = BUFFER_SIZE - recv_len;
        if(pre_read > 512) {
            pre_read = 512;
        }

        len = net_read(http->net.fd, http->buffer + recv_len, pre_read, timeoutms);
        //printf("pre read %d\\n", len);

        if (len <= 0) {
            LOGE(TAG, "net read len=%d errno=%d", len, errno);
            return -1;
        }

        recv_len += len;

        if ((content_len = http_parse_head(http, head_end)) == 0) {
            continue;
        }

        if (content_len < 0) {
            if (content_len == -2) {
                if (net_quick_reconnect(http) < 0) {
                    return 0;
                }
                return -2;
            }
            return -1;
        } else {
            break;
        }
    }

    if (content_len <= 0 && !(*head_end)) {
        goto again;
    }

    int head_len = (*head_end - (char *)http->buffer);
    int total_len = content_len + head_len;
    //int left_len = content_len + head_len - len;
    //printf("head len %d, left_len %d, total %d, recv len %d\\n", head_len, left_len, total_len, recv_len);

    if ( total_len > BUFFER_SIZE ) {
        LOGE(TAG, "ERR total len %d >BUFFER_SIZE; return -1", total_len);
        return -1;
    }
    // LOGD(TAG, "buffer: %.*s", *head_end - (char*)http_ins->buffer, http_ins->buffer);

    while(recv_len < total_len) {
        if (content_len <= recv_len - (*head_end - (char *)http->buffer)) {
            break;
        }

        len = http->net.net_read(&http->net, http->buffer + recv_len, total_len - recv_len, timeoutms);

        //printf("left read %d\\n", len);
        if (len <= 0) {
            LOGE(TAG, "net read len=%d errno=%d", len, errno);
            return -1;
        }

        recv_len += len;
    }

    return content_len;
}

char *http_head_get(http_t *http, char *key, int *length)
{
    char *temp_key;
    char *temp_value;
    char *temp_end;

    if ((temp_key = strstr((char *)http->buffer, key)) == NULL) {
        LOGD(TAG, "no key %s", key);
        return NULL;
    }

    if ((temp_value = strstr(temp_key, ":")) == NULL) {
        LOGD(TAG, "no delimiter");
        return NULL;
    }

    if ((temp_end = strstr(temp_value, "\\r\\n")) == NULL) {
        LOGD(TAG, "no end");
        return NULL;
    }

    *length = (int)(temp_end - temp_value - 2) >= 0 ? (int)(temp_end - temp_value - 2) : 0;// 1 space

    return temp_value + 1;
}

char *http_read_data(http_t *http)
{
    char *buffer;

    buffer = strstr((char *)http->buffer, "\\r\\n\\r\\n");

    if (buffer == NULL) {
        return NULL;
    }

    return buffer + 4;
}

int http_deinit(http_t *http)
{
    if (http) {
        http->net.net_disconncet(&http->net);

        if (http->url) aos_free(http->url);
        if (http->path) aos_free(http->path);
        if (http->host) aos_free(http->host);
        if (http->buffer) aos_free(http->buffer);
        aos_free(http);
    }

    return 0;
}

http.h

/*
 * Copyright (C) 2019-2020 Alibaba Group Holding Limited
 */

#ifndef __HTTP_H__
#define __HTTP_H__

#include "network.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct http {
    char *path;
    char *host;
    uint8_t *buffer;
    int buffer_offset;
    char *url;
    int port;
    network_t net;
} http_t;

// char *json_getvalue(char *body, char *key, int *len);

http_t *http_init(const char *path);
int http_head_sets(http_t *http, const char *key, const char *value);
int http_head_seti(http_t *http, const char *key, int value);
int http_post(http_t *http, char *playload, int timeoutms);
int http_get(http_t *http, int timeoutms);
int http_wait_resp(http_t *http, char **head_end, int timeoutms);
char *http_head_get(http_t *http, char *key, int *length);
char *http_read_data(http_t *http);
int http_deinit(http_t *http);
#ifdef __cplusplus
}
#endif

#endif

app_network.c

/*
 * Copyright (C) 2019-2020 Alibaba Group Holding Limited
 */
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include "aos/kernel.h"
#include "network.h"
#include <aos/debug.h>

static int net_read(network_t *n, unsigned char *buf, int count, int timeout_ms)
{
    fd_set fds;
    int lfirst = 1;
    int rc, len = 0, fd;
    struct timeval tv;
    unsigned long long delta;
    struct timeval t1 = {0}, t2 = {0};

    if (!(n && buf && count)) {
        return -1;
    }

    fd = n->fd;
    tv.tv_sec = timeout_ms / 1000;
    tv.tv_usec = (timeout_ms % 1000) * 1000;

    gettimeofday(&t1, NULL);
    while (len < count) {
        FD_ZERO(&fds);
        FD_SET(fd, &fds);
        /* FIXME: patch for select. */
        if (lfirst) {
            lfirst = 0;
        } else {
            gettimeofday(&t2, NULL);
            delta = (t2.tv_sec  - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec) / 1000;
            if (delta >= timeout_ms)
                break;
        }

        rc = select(fd + 1, &fds, NULL, NULL, &tv);
        if (rc < 0) {
            if ((errno == EINTR) || (errno == EAGAIN)) {
                aos_msleep(20);
                continue;
            }
            return -1;
        } else if (rc == 0) {
            /* time out */
            break;
        }

        if (!FD_ISSET(fd, &fds)) {
            aos_msleep(20);
            continue;
        }

        rc = recv(fd, buf + len, count - len, 0);
        if (rc < 0) {
            if ((errno == EINTR) || (errno == EAGAIN)) {
                aos_msleep(20);
                continue;
            }
            return -1;
        } else if (rc == 0) {
            /* the sockfd may be closed */
            break;
        }

        len += rc;
    }

    return len;
}

/**
 * @brief write n bytes from a sockfd with timeout
 * @param  [in] fd
 * @param  [in] buf
 * @param  [in] count
 * @param  [in] timeout_ms
 * @return -1 on err
 */
static int net_write(network_t *n, unsigned char *buf, int count, int timeout_ms)
{
    fd_set fds;
    int lfirst = 1;
    int rc, len = 0, fd;
    struct timeval tv;
    unsigned long long delta;
    struct timeval t1 = {0}, t2 = {0};

    if (!(n && buf && count)) {
        return -1;
    }

    fd = n->fd;
    tv.tv_sec = timeout_ms / 1000;
    tv.tv_usec = (timeout_ms % 1000) * 1000;

    gettimeofday(&t1, NULL);
    while (len < count) {
        FD_ZERO(&fds);
        FD_SET(fd, &fds);
        /* FIXME: patch for select. */
        if (lfirst) {
            lfirst = 0;
        } else {
            gettimeofday(&t2, NULL);
            delta = (t2.tv_sec  - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec) / 1000;
            if (delta >= timeout_ms)
                break;
        }

        rc = select(fd + 1, NULL, &fds, NULL, &tv);
        if (rc < 0) {
            if ((errno == EINTR) || (errno == EAGAIN)) {
                aos_msleep(20);
                continue;
            }
            return -1;
        } else if (rc == 0) {
            /* time out */
            break;
        }

        if (!FD_ISSET(fd, &fds)) {
            aos_msleep(20);
            continue;
        }

        rc = sendto(fd, buf + len, count - len, 0,
                    (struct sockaddr *)&(n->address),
                    sizeof(n->address));
        if (rc < 0) {
            if ((errno == EINTR) || (errno == EAGAIN)) {
                aos_msleep(20);
                continue;
            }
            return -1;
        } else if (rc == 0) {
            /* the sockfd may be closed */
            break;
        }

        len += rc;
    }

    return len;
}

static int net_connect(network_t *n, char *addr, int port, int net_type)
{
    int rc = -1;
    sa_family_t family = AF_INET;
    struct addrinfo *result = NULL;
    struct addrinfo hints = {0, AF_UNSPEC, net_type, 0, 0, NULL, NULL, NULL};

    if ((rc = getaddrinfo(addr, NULL, &hints, &result)) == 0) {
        struct addrinfo *res = result;

        /* prefer ip4 addresses */
        while (res) {
            if (res->ai_family == AF_INET) {
                result = res;
                break;
            }

            res = res->ai_next;
        }

        if (result->ai_family == AF_INET) {
            n->address.sin_port = htons(port);
            n->address.sin_family = family = AF_INET;
            n->address.sin_addr = ((struct sockaddr_in *)(result->ai_addr))->sin_addr;
        } else {
            rc = -1;
        }

        freeaddrinfo(result);
    }

    if (rc == 0) {
        n->fd = socket(family, net_type, 0);

        if (n->fd != -1) {
            rc = connect(n->fd, (struct sockaddr *)&n->address, sizeof(n->address));
        }

        if (rc < 0) {
            close(n->fd);
            n->fd = 0;
        }
    }

    return rc;
}

static void net_disconnect(network_t *n)
{
    close(n->fd);
    n->fd = 0;
}

void network_init(network_t *n)
{
    n->fd = 0;
    n->net_read       = net_read;
    n->net_write      = net_write;
    n->net_connect    = net_connect;
    n->net_disconncet = net_disconnect;
}

network.h

/*
 * Copyright (C) 2019-2020 Alibaba Group Holding Limited
 */

#ifndef __NETWORK_H__
#define __NETWORK_H__

#include <sys/socket.h>
#include <arpa/inet.h>
#include <lwip/netdb.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct network {
    int fd;
    struct sockaddr_in address;
    int (*net_connect)(struct network *n, char *addr, int port, int net_type);
    int (*net_read)(struct network *, unsigned char *, int, int);
    int (*net_write)(struct network *, unsigned char *, int, int);
    void (*net_disconncet)(struct network *n);
} network_t;

void network_init(network_t *n);

#ifdef __cplusplus
}
#endif
#endif

三、进行http get请求,获取天气信息

有了http get的接口,又有了我们上一篇文章里可以获取到天气信息的URL,让我们开心的http get请求吧。

  1. 这里直接贴请求天气的接口函数 static int aos_http_get_weather(void)
static int aos_http_get_weather(void) {
    http_t *http;
    char *payload;
    char *body;
    int value_len;
    char *cptr;
    char getvalue[64];
    int rc;

    memset(getvalue, 0, sizeof(getvalue));
    get_weather_url(getvalue, sizeof(getvalue));

    if ((http = http_init(getvalue)) == NULL) {
        LOGD(TAG, "error http init");
        aos_free(payload);
        return -1;
    }
    memset(http->buffer, 0, 2048);
    http->buffer_offset = 0;
    http_head_sets(http, "Host", http->host);
    http_head_seti(http, "Content-Length", strlen(payload));
    http_head_sets(http, "Connection", "keep-alive");
    http_head_sets(http, "Content-Type", "application/json");
    http_head_sets(http, "Cache-Control", "no-cache");

    http_get(http, 10000);

    LOGD(TAG, "resp body: %200s", body);

    parse_http_data(body);

    http_deinit(http);
    return 0;
}
  1. 其中 函数static void parse_http_data(char *http_data),就是解析 获取天气信息JSON的函数,这个上一篇cJSON的介绍和使用了有讲解,可以翻看之前的内容。

 

四、感想

  1. 其实YoC的很多内容就是alios-things,这些年看到组件越来越丰富,越来越好用,真的很高心。
  2. 还记得很早以前,同事告诉我看网络,可以看看netmgr组件,一晃好多年过去了,可惜我还是菜鸡,希望可以越来越好。
  3. 基本上这个项目到这里就进入尾声吧,虽然想试试音频部分,但觉得以后可以用其他的项目跑,暂时又因为最近生活发生了很多不好的事情,还需要花时间去面对,没有心思和精力去再深入的玩一下,加油了,大家!

最新回复

谢谢楼主了,获取天气这块,搞得我焦头烂额   详情 回复 发表于 2022-5-4 11:13
点赞 关注
 
 

回复
举报

2万

帖子

74

TA的资源

管理员

沙发
 

期待楼主遇到的问题顺利解决。

加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身

点评

谢谢,希望如此  详情 回复 发表于 2022-4-29 07:56
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 
 

回复

41

帖子

2

TA的资源

一粒金砂(中级)

板凳
 
soso 发表于 2022-4-28 09:26 期待楼主遇到的问题顺利解决。

谢谢,希望如此

 
 
 

回复

7046

帖子

11

TA的资源

版主

4
 

太好了,可以借用http://post 可以用上我的服务器了。不过没有mqtt,还是不好玩。

点评

我移植的代码里有http post,获取天气使用的是http get,两个还是有区别的,http post的demo,你可以看下那个OTA的  详情 回复 发表于 2022-4-29 17:17
 
 
 

回复

41

帖子

2

TA的资源

一粒金砂(中级)

5
 
lugl4313820 发表于 2022-4-29 16:47 太好了,可以借用http://post 可以用上我的服务器了。不过没有mqtt,还是不好玩。

我移植的代码里有http post,获取天气使用的是http get,两个还是有区别的,http post的demo,你可以看下那个OTA的

点评

大早起来,跟楼主把代码手工录一遍,争取今天完成http post,祝大家五一开心,看来我是要”劳动“一天了。加油!  详情 回复 发表于 2022-5-1 07:02
 
 
 

回复

7046

帖子

11

TA的资源

版主

6
 
小默叔叔 发表于 2022-4-29 17:17 我移植的代码里有http post,获取天气使用的是http get,两个还是有区别的,http post的demo,你可以看下 ...

大早起来,跟楼主把代码手工录一遍,争取今天完成http post,祝大家五一开心,看来我是要”劳动“一天了。加油!

 
 
 

回复

1万

帖子

16

TA的资源

版主

7
 

谢谢楼主了,获取天气这块,搞得我焦头烂额

个人签名http://shop34182318.taobao.com/
https://shop436095304.taobao.com/?spm=a230r.7195193.1997079397.37.69fe60dfT705yr
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表