基于ESP32的全功能(uart+mqtt+TCP/IP)透传实现

目的:更廉价的物联网仪器解决方案。

当前方案:配置串口安卓平板。

-优势:开发简单,安卓一大把人,好维护。操作人员熟悉操作方式。学习成本低。
-劣势:成本巨高,推广困难。仪器必须预留平板位置。

ESP方案:Bluff配网+Mqtt+tcp/IP+uart

-优势:真便宜啊。占地更小,尤其小仪器。
-劣势:找人很费劲,尤其小团队。用的还不是stm。

实现方式:糅合和官方各种案例,包括,blufi,uart publish,mqtt,socks等样例。(码农原来是这个样子,抄过来整合一下。。)

以下代码是老板亲自糅合的,自己现学现用。代码规范啥的,大家多多给指点以下。一定及时修改更新。以下代码方案完全给大家贡献出来。

PASS:我这边传递的都是stm直接使用的串口指令。所以都是十六进制的BYTE字符串。所以如果有用到传递其他字符串的。需要各位手动修改。代码太多了,就贴重点了。具体代码大家私信我要。人多的话,我再考虑上传git,毕竟有点献丑。。

1.首先:串口接收。
static void uart_event_task(void *pvParameters)
...

uart_event_t event;
size_t buffered_size;
uint8_t* dtmp = (uint8_t*) malloc(RD_BUF_SIZE);
char *hexstr_data = malloc(RX_BUF_SIZE);
BLUFI_INFO("for uart event loop begin:");
for(;;) 
{
    //Waiting for UART event.
    if(xQueueReceive(uart_queue, (void * )&event, (portTickType)portMAX_DELAY)) 
    {
        bzero(dtmp, RD_BUF_SIZE);
        ESP_LOGI(TAG, "uart[%d] event:", EX_UART_NUM);
        switch(event.type) {
            //Event of UART receving data
            /*We'd better handler data event fast, there would be much more data events than
            other types of events. If we take too much time on data event, the queue might
            be full.*/
            case UART_DATA:
                ESP_LOGI(TAG, "[UART DATA]: %d", event.size);
                const int rxBytes = uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY);
                //send to mqtt server
                if (rxBytes > 0 && mqtt_is_connected ) {
                    dtmp[rxBytes] = 0;
                    ESP_LOGW(TAG, "Read %d bytes: '%s'", rxBytes, dtmp);
                    esp_log_buffer_hex("Receive Data", dtmp, rxBytes);
                    int cir_len= sizeof(*dtmp) / sizeof(dtmp[0]);
                    ESP_LOGI(TAG, "recv data lenth is:%d\n",cir_len);
                    if ( cir_len > 1 ){
                        byte_str_hex(dtmp,hexstr_data,cir_len);
                        // BLUFI_INFO("event publish data is:%s\n",hexstr_data);
                        if(tcp_socked){
                            tcp_tx_data = hexstr_data;
                        }else{
                            int msg_id = mqtt_send_data(TOPIC_UART,hexstr_data);
                            ESP_LOGI(TAG, "sent publish successful, msg_id=%d\n", msg_id);
                        }

                    }
                }
                ESP_LOGI(TAG, "[DATA EVT]:");
                uart_write_bytes(EX_UART_NUM, (const char*) dtmp, event.size);
                break;
            //Event of HW FIFO overflow detected
            case UART_FIFO_OVF:
                ESP_LOGI(TAG, "hw fifo overflow");
                // If fifo overflow happened, you should consider adding flow control for your application.
                // The ISR has already reset the rx FIFO,
                // As an example, we directly flush the rx buffer here in order to read more data.
                uart_flush_input(EX_UART_NUM);
                xQueueReset(uart_queue);
                break;
            //Event of UART ring buffer full
            case UART_BUFFER_FULL:
                ESP_LOGI(TAG, "ring buffer full");
                // If buffer full happened, you should consider encreasing your buffer size
                // As an example, we directly flush the rx buffer here in order to read more data.
                uart_flush_input(EX_UART_NUM);
                xQueueReset(uart_queue);
                break;
            //Event of UART RX break detected
            case UART_BREAK:
                ESP_LOGI(TAG, "uart rx break");
                break;
            //Event of UART parity check error
            case UART_PARITY_ERR:
                ESP_LOGI(TAG, "uart parity error");
                break;
            //Event of UART frame error
            case UART_FRAME_ERR:
                ESP_LOGI(TAG, "uart frame error");
                break;
            //UART_PATTERN_DET
            case UART_PATTERN_DET:
                uart_get_buffered_data_len(EX_UART_NUM, &buffered_size);
                int pos = uart_pattern_pop_pos(EX_UART_NUM);
                ESP_LOGI(TAG, "[UART PATTERN DETECTED] pos: %d, buffered size: %d", pos, buffered_size);
                if (pos == -1) {
                    // There used to be a UART_PATTERN_DET event, but the pattern position queue is full so that it can not
                    // record the position. We should set a larger queue size.
                    // As an example, we directly flush the rx buffer here.
                    uart_flush_input(EX_UART_NUM);
                } else {
                    uart_read_bytes(EX_UART_NUM, dtmp, pos, 100 / portTICK_PERIOD_MS);
                    uint8_t pat[PATTERN_CHR_NUM + 1];
                    memset(pat, 0, sizeof(pat));
                    uart_read_bytes(EX_UART_NUM, pat, PATTERN_CHR_NUM, 100 / portTICK_PERIOD_MS);
                    ESP_LOGI(TAG, "read data: %s", dtmp);
                    ESP_LOGI(TAG, "read pat : %s", pat);
                }
                break;
            //Others
            default:
                ESP_LOGI(TAG, "uart event type: %d", event.type);
                break;
        }

    }
}
free(hexstr_data);
free(dtmp);
dtmp = NULL;
vTaskDelete(NULL);

......
2.其次:MQTT接收。
static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
...

static const char *MQTT_TASK_TAG = "MQTT_TASK";
esp_log_level_set(MQTT_TASK_TAG, ESP_LOG_INFO);
esp_mqtt_client_handle_t client = event->client;
int msg_id;
char mqtt_subscribe_topic[128];
sprintf(mqtt_subscribe_topic,"%s%s%s","/",ble_mac_str,"/#");
//inert code from publish
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
    ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
    xEventGroupSetBits(mqtt_event_group, CONNECTED_BIT);
    mqtt_is_connected = true;
    BLUFI_INFO("subscribe topic=%s",mqtt_subscribe_topic);
    msg_id = esp_mqtt_client_subscribe(client, mqtt_subscribe_topic, 1);
    ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
    break;
//MQTT disconnection
case MQTT_EVENT_DISCONNECTED:
    ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
    xEventGroupClearBits(mqtt_event_group, CONNECTED_BIT);
    mqtt_is_connected = false;
    break;
//MQTT send subscribed
case MQTT_EVENT_SUBSCRIBED:
    ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
    msg_id = mqtt_send_data(TOPIC_HEAD,"successful subscribe");
    ESP_LOGI(TAG, "sent publish finished, msg_id=%d", msg_id);
    BLUFI_INFO("mqtt start and subcribe then send ip\n");
    send_ip();
    break;
//MQTT unsubscribed
case MQTT_EVENT_UNSUBSCRIBED:
    ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
    break;
//MQTT publish topic
case MQTT_EVENT_PUBLISHED:
    ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
    break;
//MQTT accept data
case MQTT_EVENT_DATA:
    ESP_LOGI(TAG, "MQTT_EVENT_DATA");
    printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
    printf("DATA=%.*s\r\n", event->data_len, event->data);
    printf("ID=%d, total_len=%d, data_len=%d, current_data_offset=%d\n", event->msg_id, event->total_data_len, event->data_len, event->current_data_offset);
    //send to uart
    if( uart_is_connected && strncmp((event->topic),TOPIC_START,strlen(TOPIC_START)) == 0 ) {
        // uart_tx_str = malloc(event->data_len);
        memcpy(uart_tx_str,(char *)event->data,event->data_len);
        uart_tx_str[event->data_len]='\0';
        BLUFI_INFO("MQTT TO UART DATA: %s\n",uart_tx_str);
        msg_id = mqtt_send_data( TOPIC_CHECK,"equ starting");
    }else{
    ESP_LOGI(TAG, "MQTT unconnected or topic is not wright code, DATA=%.*s\r\n",event->data_len,event->data);
    }
    msg_id = mqtt_send_data( TOPIC_CHECK,"65831");
    break;
case MQTT_EVENT_ERROR:
    ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
    xEventGroupClearBits(mqtt_event_group, CONNECTED_BIT);
    break;
case MQTT_EVENT_BEFORE_CONNECT:
    ESP_LOGI(TAG, "MQTT_EVENT_BEFORE_CONNECT");
    xEventGroupClearBits(mqtt_event_group, CONNECTED_BIT);
    break;
default:
    ESP_LOGI(TAG, "Other event id:%d", event->event_id);
    break;
}
return ESP_OK;

...

3.其次:TCP/IP接收。(这里有个坑,本来打算用 #ifdef #else #endif 来把代码缩减一下。一直调试不通。。然后直接把IP6,注释掉了。。)
static void tcp_server_task(void *pvParameters)
...

char rx_buffer[128];
char addr_str[128];
int addr_family;
int ip_protocol;
while (1) {
    // if(IP_TYPE == AF_INET6){
    //     struct sockaddr_in6 dest_addr;
    //     ip6_addr_t ip6_info;
    //     bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
    //     dest_addr.sin6_family = IP_TYPE;
    //     dest_addr.sin6_port = htons(PORT);
    //     addr_family = IP_TYPE;
    //     ip_protocol = IPPROTO_IPV6;
    //     inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
        
    //     int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    //     if (listen_sock < 0) {
    //         ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
    //         break;
    //     }
    //     ESP_LOGI(TAG, "Socket created");

    //     int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    //     if (err != 0) {
    //         ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
    //         break;
    //     }
    //     ESP_LOGI(TAG, "Socket bound, port %d", PORT);

    //     err = listen(listen_sock, 1);
    //     if (err != 0) {
    //         ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
    //         break;
    //     }
    //     ESP_LOGI(TAG, "Socket listening");
        
    //     // Large enough for both IPv4 or IPv6
    //     struct sockaddr_in6 source_addr; 
    //     uint addr_len = sizeof(source_addr);
    //     int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
    //     if (sock < 0) {
    //         ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
    //         break;
    //     }
    //     ESP_LOGI(TAG, "Socket accepted");

    //     //IP to mqtt server
    //     tcp_socked = true;
    //     err = tcpip_adapter_get_ip6_linklocal(TCPIP_IF,&ip6_info);
    //     char * ptr = inet_ntop(IP_TYPE, &ip6_info, ip_addr_str, sizeof(ip6_addr_t));
    //     int msg_id= mqtt_send_data(TOPIC_TCP,ip_addr_str);
    //     ESP_LOGI(TAG, "id=%d\r\nipstr=%s", msg_id, ip_addr_str);
    // }else{
    struct sockaddr_in dest_addr;
    dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    dest_addr.sin_family = IP_TYPE;
    dest_addr.sin_port = htons(PORT);
    addr_family = IP_TYPE;
    ip_protocol = IPPROTO_IP;
    inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);

    int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        break;
    }
    ESP_LOGI(TAG, "Socket created");

    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err != 0) {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        break;
    }
    ESP_LOGI(TAG, "Socket bound, port %d", PORT);

    err = listen(listen_sock, 1);
    if (err != 0) {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        break;
    }
    ESP_LOGI(TAG, "Socket listening");
    
    // Large enough for both IPv4 or IPv6
    struct sockaddr_in6 source_addr; 
    uint addr_len = sizeof(source_addr);
    int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
    if (sock < 0) {
        ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
        break;
    }
    ESP_LOGI(TAG, "Socket accepted");

    //IP to mqtt server
    tcp_socked = true;
    // }

    while (1) {
        int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        // Error occurred during receiving
        if (len < 0) {
            ESP_LOGE(TAG, "recv failed: errno %d", errno);
            break;
        }
        // Connection closed
        else if (len == 0) {
            ESP_LOGI(TAG, "Connection closed");
            tcp_socked = false;
            break;
        }
        // Data received
        else {
            // Get the sender's ip address as string
            if (source_addr.sin6_family == PF_INET) 
            {
                inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
            } else if (source_addr.sin6_family == PF_INET6) 
            {
                inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
            }
            // Null-terminate whatever we received and treat like a string
            rx_buffer[len] = 0; 
            ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
            ESP_LOGI(TAG, "%s", rx_buffer);
            
            //send tcp_data to uart
            uart_tx_str = rx_buffer;

            //send date by tcp
            // int err = send(sock, tcp_tx_data, strlen(tcp_tx_data), 0);
            int err = tcp_send_data(sock,rx_buffer,strlen(rx_buffer));
            if (err < 0)
             {
                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                break;
            }
        }
    }

    if (sock != -1) 
    {
        ESP_LOGE(TAG, "Shutting down socket and restarting...");
        shutdown(sock, 0);
        close(sock);
    }
}
vTaskDelete(NULL);

...

4.WIFI获得IP后的事件:这里时机很重要。我之前放在独立去初始化后,启动mqtt以及tcpserver的。发现有问题。我改到了WIFI获取到IP事件后。就可以了。
static esp_err_t blufi_net_event_handler(void *ctx, system_event_t *event)
...

wifi_mode_t mode;
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
    esp_wifi_connect();
    break;
case SYSTEM_EVENT_STA_GOT_IP: {
    esp_blufi_extra_info_t info;
    xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
    esp_wifi_get_mode(&mode);
    memset(&info, 0, sizeof(esp_blufi_extra_info_t));
    memcpy(info.sta_bssid, gl_sta_bssid, 6);
    info.sta_bssid_set = true;
    info.sta_ssid = gl_sta_ssid;
    info.sta_ssid_len = gl_sta_ssid_len;
    if (ble_is_connected == true) {
        esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info);
    } else {
        BLUFI_INFO("BLUFI BLE is not connected yet\n");
    }
    // check wifi link mqtt
    if( !mqtt_is_connected ){
        BLUFI_INFO("mqtt start\n");
        init_mqtt();//MQTT sart
        BLUFI_INFO("tcpsever_start\n");
        tcpsever_start();
    } else {
        BLUFI_INFO("MQTT connected yet,evnetid=%d\n",event->event_id);
    }
    break;
}
case SYSTEM_EVENT_STA_CONNECTED:
    gl_sta_connected = true;
    memcpy(gl_sta_bssid, event->event_info.connected.bssid, 6);
    memcpy(gl_sta_ssid, event->event_info.connected.ssid, event->event_info.connected.ssid_len);
    gl_sta_ssid_len = event->event_info.connected.ssid_len;
    break; 
case SYSTEM_EVENT_STA_DISCONNECTED:
    /* This is a workaround as ESP32 WiFi libs don't currently
    auto-reassociate. */
    gl_sta_connected = false;
    memset(gl_sta_ssid, 0, 32);
    memset(gl_sta_bssid, 0, 6);
    gl_sta_ssid_len = 0;
    esp_wifi_connect();
    xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
    break;
case SYSTEM_EVENT_AP_START:
    esp_wifi_get_mode(&mode);
    /* TODO: get config or information of softap, then set to report extra_info */
    if (ble_is_connected == true) {
        if (gl_sta_connected) {  
            esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, NULL);
        } else {
            esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL);
        }
    } else {
        BLUFI_INFO("BLUFI BLE is not connected yet\n");
    }
    break;
case SYSTEM_EVENT_SCAN_DONE: {
    uint16_t apCount = 0;
    esp_wifi_scan_get_ap_num(&apCount);
    if (apCount == 0) {
        BLUFI_INFO("Nothing AP found");
        break;
    }
    wifi_ap_record_t *ap_list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount);
    if (!ap_list) {
        BLUFI_ERROR("malloc error, ap_list is NULL");
        break;
    }
    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&apCount, ap_list));
    esp_blufi_ap_record_t * blufi_ap_list = (esp_blufi_ap_record_t *)malloc(apCount * sizeof(esp_blufi_ap_record_t));
    if (!blufi_ap_list) {
        if (ap_list) {
            free(ap_list);
        }
        BLUFI_ERROR("malloc error, blufi_ap_list is NULL");
        break;
    }
    for (int i = 0; i < apCount; ++i)
    {
        blufi_ap_list[i].rssi = ap_list[i].rssi;
        memcpy(blufi_ap_list[i].ssid, ap_list[i].ssid, sizeof(ap_list[i].ssid));
    }
    
    if (ble_is_connected == true) {
        //send result
        esp_blufi_send_wifi_list(apCount, blufi_ap_list);
    } else {
        BLUFI_INFO("BLUFI BLE is not connected yet\n");
    }
    esp_wifi_scan_stop();
    free(ap_list);
    free(blufi_ap_list);
    break;
    }
default:
    break;
}
return ESP_OK;

...

5.其他,之后的都不是重点了,我把结构给大家贴出来。
...

static esp_mqtt_client_config_t mqtt_cfg = {
static esp_ble_adv_data_t ble_adv_data = {
static esp_ble_adv_params_t ble_adv_params = {
static void init_mqtt(void);

static char hex_char(char cstr)
int hexstr_to_hexbyte( char *hexstr, char *bytestr)
void byte_str_hex( uint8_t *sSrc, char *sDest,int nSrcLen )  
int uart_send_data(const char* logName, const char* data,size_t size)
void tx_task()
int mqtt_send_data(const char *topic_str,char *data )
int tcp_send_data(int s,void *data,size_t size)
static void tcp_server_task(void *pvParameters)
static void send_ip()
static void tcpsever_start()
static void uart_event_task(void *pvParameters)
static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param)
static void ble_gap_event_adv_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
static esp_err_t blufi_net_event_handler(void *ctx, system_event_t *event)
static esp_blufi_callbacks_t security_callbacks = {
static void init_mqtt(void){
static void set_ble_adv(void){
static void init_blufi(void){
static void init_wifi(void)
static void init_uart(void){
void app_main()
{
esp_err_t ret;

/* init NVS
 */
ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
    ESP_ERROR_CHECK(nvs_flash_erase());
    ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );

/* link to MCU
 */
BLUFI_INFO("UART start initialise,please wait a miniutes\n");
init_uart();
BLUFI_INFO("UART finished initialise\n");
/* link to wifi
 */
BLUFI_INFO("WIFI start initialise,please wait a miniutes\n");
init_wifi();
BLUFI_INFO("wifi finished initialise\n");

/* open the ble
 */
BLUFI_INFO("BLE start initialise,please wait a miniutes\n");
init_blufi();
BLUFI_INFO("BLE finished initialise\n");
}

...

好了,基本就是这样。如果大家有需要私信我吧。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容