2103 views|2 replies

155

Posts

1

Resources
The OP
 

[Sipeed LicheeRV 86 Panel Review] 11. Implementing TCP Client with lvgl and fork() [Copy link]

 This post was last edited by sonicfirr on 2022-4-12 15:35

This article records the process of implementing TCP Client on the Tina system, and combines lvgl to realize opening a TCP connection by clicking a screen button. After connecting to the TCP Server , start the lvgl timer, periodically send strings to the Server , and create a child process to receive the strings sent by the Server .

/* Includes ------------------------------------------------------- */
#include "lvgl/lvgl.h"
#include "lv_drivers/display/fbdev.h"
#include "lv_drivers/indev/evdev.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


/* Private macro -------------------------------------------------- */
#define AITA_DISP_BUF_SIZE     (128 * 1024)
#define AITA_SCREEN_WIDTH      480
#define AITA_SCREEN_HEIGHT     480
#define AITA_TITLE_STRING      "AITA DEMO for LicheeRV with LVGL -- TCP Client"
#define N                      128
#define OFF                    0
#define ON                     1
#define SERVER_IP              "192.168.31.8"
#define SERVER_PORT            1234
#define SEND_PERIOD            1000
#define SEND_MSG               "Hello World!\n"
#define errlog(errmsg)         do{ perror(errmsg);\
	printf("----%s----%s----%d----\n", __FILE__, __func__, __LINE__);\
	return;\
	} while(0)


/* Global variables ----------------------------------------------- */
lv_indev_t *aita_indev;   //pointer of indev
lv_obj_t *sys_scr;        //pointer of system screen instance
lv_obj_t *head_label;     //pointer of title label instance
lv_obj_t *main_label;     //pointer of main label instance
lv_obj_t *conn_btn;       //pointer of conn button(tcp client) for instance
lv_obj_t *conn_btn_label; //pointer of label instance for conn button
lv_timer_t *tcp_timer;    //pointer of timer instance for tcp polling
int conn_flag = OFF;      //flag of tcp connect status
int sockfd;               //socket file descriptor
struct sockaddr_in server;//tcp server ip;
socklen_t addrlen = sizeof(server);
int tcptimer_counter = 0; //send msg per (tcptimer_counter * SEND_PERIOD) ms
char recv_buf[N] = "";    //recv buffer
pid_t pid;                //recv process id


/* Private function prototypes ------------------------------------ */
void aita_InitLVGL(void);
void aita_CreateMainUI(void);
void aita_InitTimer(void);
void aita_InitSocket(void);
void conn_btn_click_cb(lv_event_t *e);
void tcp_timer_cb(lv_timer_t *timer);


/* Private functions ---------------------------------------------- */
int main(void) {
//by author. initialize lvgl including displaybuffer, device for disp & input
    aita_InitLVGL();
    
//by author. initialize and register event device
//these code must be in main(), otherwise the touch will fail
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER; //by author. input device type, choice touchpad here
    indev_drv.read_cb = evdev_read;         //by author. callback to read input device data
    aita_indev = lv_indev_drv_register(&indev_drv);

//by author. create the main view when the demo starts up    
    aita_CreateMainUI();

//by author. create a timer
    aita_InitTimer();
    
    /*Handle LitlevGL tasks (tickless mode)*/
    while(1) {
        lv_task_handler();
        usleep(5000);
    }

    return 0;
}

/*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/
uint32_t custom_tick_get(void)
{
    static uint64_t start_ms = 0;
    if(start_ms == 0) {
        struct timeval tv_start;
        gettimeofday(&tv_start, NULL);
        start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
    }

    struct timeval tv_now;
    gettimeofday(&tv_now, NULL);
    uint64_t now_ms;
    now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;

    uint32_t time_ms = now_ms - start_ms;
    return time_ms;
}

void aita_InitLVGL(void) {
    /*LittlevGL init*/
    lv_init();

    /*Linux frame buffer device init*/
    fbdev_init(); //by author. initialize framebuffer device for display
    evdev_init(); //by author. initialize event device for touchpad

    /*A small buffer for LittlevGL to draw the screen's content*/
    static lv_color_t buf[AITA_DISP_BUF_SIZE];
    /*Initialize a descriptor for the buffer*/
    static lv_disp_draw_buf_t disp_buf;
    lv_disp_draw_buf_init(&disp_buf, buf, NULL, AITA_DISP_BUF_SIZE);
    /*Initialize and register a display driver*/
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.draw_buf   = &disp_buf;
    disp_drv.flush_cb   = fbdev_flush;
    disp_drv.hor_res    = 480;
    disp_drv.ver_res    = 480;
    lv_disp_drv_register(&disp_drv);
}

void aita_CreateMainUI(void) {
    //by author. create system screen which is basic graphic level
    sys_scr = lv_obj_create(lv_scr_act());
    lv_obj_set_size(sys_scr, AITA_SCREEN_WIDTH, AITA_SCREEN_HEIGHT);
    //by author. create the main title which is just a label
    head_label = lv_label_create(sys_scr);
    lv_label_set_text(head_label, AITA_TITLE_STRING);
    lv_obj_align(head_label, LV_ALIGN_TOP_MID, 0, 50);

    char main_label_text_buf[64];
    sprintf(main_label_text_buf, "server ip:%s\nserver port:%d", SERVER_IP, SERVER_PORT);
    main_label = lv_label_create(sys_scr);
    lv_label_set_text(main_label, main_label_text_buf);
    lv_obj_align(main_label, LV_ALIGN_CENTER, 0, 0);
    lv_obj_set_style_text_font(main_label, &lv_font_montserrat_40, 0);

    //by author. create the conn button for connecting a tcp server
    conn_btn = lv_btn_create(sys_scr);
    lv_obj_set_size(conn_btn, 200, 50);
    lv_obj_align(conn_btn, LV_ALIGN_BOTTOM_MID, 0, -50);
    //by author. register clicked-event callback for the "conn_btn" object
    lv_obj_add_event_cb(conn_btn, conn_btn_click_cb, LV_EVENT_CLICKED, NULL);
    conn_btn_label = lv_label_create(conn_btn);
    lv_label_set_text(conn_btn_label, "Connect");
    lv_obj_center(conn_btn_label);
}


//by author. tcp_timer callback which sends msg to server
void tcp_timer_cb(lv_timer_t *timer) {    
    if(send(sockfd, SEND_MSG, sizeof(SEND_MSG), 0) < 0) errlog("send error");
}
//by author. initialize timer for periodic sending
void aita_InitTimer(void) {
    tcp_timer = lv_timer_create(tcp_timer_cb, 1000, NULL);
    lv_timer_set_repeat_count(tcp_timer, -1);
    lv_timer_pause(tcp_timer);
}
//by author. initialize the socket
void aita_InitSocket(void) {
    if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0) errlog("socket error");
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(SERVER_IP);
    server.sin_port = htons(SERVER_PORT);
    printf("sockfd: %d\n", sockfd);
}
//by author. conn_btn callback which conn/disconn server
void conn_btn_click_cb(lv_event_t *e) {
    printf("Conn Btn Clicked\n");
    if(conn_flag == OFF) {
        aita_InitSocket();
        printf("server ip: %s, port: %d\n", SERVER_IP, SERVER_PORT);
        if(connect(sockfd, (struct sockaddr*)&server, addrlen) < 0) errlog("connect error");
        printf("connected\n");
        lv_timer_resume(tcp_timer);
        conn_flag = ON;
        lv_label_set_text(conn_btn_label, "Disconnect");
        //by author. create a process for tcp recv
        pid = fork();
        if(pid < 0) {
            errlog("fork error");
        } else if(pid == 0) {
            while(1) {
                if(read(sockfd, recv_buf, N) < 0) errlog("recv error");
                printf("server: %s\n", recv_buf);
                memset(recv_buf, 0, N);
            }
        }
    } else {
        lv_timer_pause(tcp_timer);
        close(sockfd);
        conn_flag = OFF;
        lv_label_set_text(conn_btn_label, "Connect");
    }
}

Notes on code design:

1 ) lvgl is not thread-safe, so a process is created here to read the socket buffer. Initially, I wanted to use a 10ms timer, but I found that read() or another function recv() are blocking. Once called, they will wait for data to be received, which will affect the UI refresh.

2 ) Personally, I think that using fork() to create a process is not safe either, because the child process is a copy of the parent process. If the program can jump out of the receiving dead loop block, it may also jump to the code related to the lvgl component, and then there will be competition for peripheral resources (the above view is only obtained through analysis and has not been verified). Of course, there should be another multi-process method, that is, to create a TCP communication application separately, and then use execl() and other exec group functions to create a process with an independent code segment (this idea will be verified later when there is an opportunity).

3 ) In addition, the information received by the child process is only output to the console. If it is to be displayed on the screen, it needs to be passed to the parent process (the main program segment of the case) through process communication. This also shows the shortcomings of lvgl . As a GUI library mainly used for MCU , it lacks support for multi-threading.

4 ) In terms of subsequent work direction, I plan to focus on learning the Linux API first , and try to use Linux 's own multi-threaded framework, while loading lvgl and socket communication to achieve a better fit.

This post is from Domestic Chip Exchange

Latest reply

Is the screen drawn directly using LVGL under Linux?   Details Published on 2022-4-12 16:22
 
 

7422

Posts

2

Resources
2
 

Is the screen drawn directly using LVGL under Linux?

This post is from Domestic Chip Exchange

Comments

Yes, there is a transplantation case shared by blogger manhuami2007 in the forum. You can find it by searching lvgl in the download area. You can also check out its post: https://en.eeworld.com/bbs/thread-1197092-1-1.html  Details Published on 2022-4-12 19:11
Personal signature

默认摸鱼,再摸鱼。2022、9、28

 
 
 

155

Posts

1

Resources
3
 
This post was last edited by sonicfirr on 2022-4-12 19:14
freebsder posted on 2022-4-12 16:22 Is the screen drawn directly using LVGL under Linux?

Yes, there is a transplantation case shared by blogger manhuami2007 in the forum . You can find it by searching lvgl in the download area.

You can also check out the post: https://en.eeworld.com/bbs/thread-1197092-1-1.html

This post is from Domestic Chip Exchange
 
 
 

Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list