Send data with a TCP Client

#1

Hi All,

  1. I am trying to create a simple TCP client program that will be able to initiate TCP messages.

  2. Here is my program:

#include "mongoose.h"
#include "mgos.h"
#include <xtensa/hal.h>
#include <math.h>
#include "string.h"
#include <esp_system.h>

const char *port1 = "tcp://192.168.4.2:1993";
//const char *port1 = "udp://192.168.4.2:1993";
struct mg_mgr mgr;
struct mg_connection *nc;

int freeheapspace; 
int freefilespace; 
int minfreeheapspace;		
int timenow; 
int timeup;										
int i=0;
char messtring[2048];
char numstring[12];
uintptr_t swtt = 0; 											

static IRAM void led_timer_cb(void *arg) {	
	timeup = (int) mgos_uptime(); 
        timenow = (int) time(0); 							
	printf("System Up Time:  %d  Time Now  %d \r\n",timeup,timenow);
	messtring[0]='\0';	
	strcpy(messtring,"Time Up:     "); sprintf(numstring,"%d",timeup); strcat(messtring,numstring);
	strcat(messtring,"   Time Now:     ");sprintf(numstring,"%d",timenow);strcat(messtring,numstring);
	if (timeup>60) {
            mg_send(nc, messtring, strlen(messtring));
        }			
	mg_mgr_poll(&mgr,100);							
	(void) arg;
}

enum mgos_app_init_result mgos_app_init(void) {			
	mg_mgr_init(&mgr, NULL);										
	nc = mg_connect(&mgr, port1, NULL,NULL);					
	swtt= mgos_set_timer(5000, MGOS_TIMER_REPEAT, led_timer_cb, NULL);	
	return MGOS_APP_INIT_SUCCESS;
}
  1. With UDP selected, I get a 5 second interval log of the time. With TCP selected, the ESP 32 crashes.

  2. What is the problem? What is the simplest code that will achieve this simple end?

Thanks for your help,

JSW

#2

To clarify: I am using Packet Sender as my UDP/TCP server. ESP32 is configured as AP at 192.168.4.1.

#3

There are several problems with your code

  • don’t create a new mgr (mg_mgr_init(&mgr, NULL)). Use the existing one with mgos_get_mgr()
  • remove the mg_mgr_poll(&mgr,100) line
  • create the tcp connection AFTER the client (192.168.4.2) is connected
  • enable Persistent TCP connections in Packet Sender.
#4

Hi nliviu, Thanks for your help.

I was able to get my submitted example program to work by delaying the mg_connect function call until after 192.168.4.2 is certainly connected AND using persistent connections in Packet Sender.

I could not get anything to work (delayed mg_connect or not) if I use mgos_get_mgr in place of mg_mgr_init (immediate crash) OR if I remove the mg_mgr_poll function call (no crash, but no message). Is there some reason to dig deeper into getting mgos_get_mgr to work? Or should I declare victory and move on?

Thanks,

JSW

#5

Here is my working code. Tested in STA mode, but it should work in AP mode, too.

#include "mgos.h"

#include "mgos_wifi.h"

struct app_ctx {
  struct mg_connection *nc;
  mgos_timer_id timer_id;
};

static void led_timer_cb(void *arg) {
  int uptime = (int) mgos_uptime();
  int now = (int) time(0);
  char messtring[128];
  snprintf(messtring, sizeof(messtring), "Uptime: %10d, now: %10d", uptime,
           now);
  LOG(LL_INFO, ("%s", messtring));

  struct app_ctx *ctx = (struct app_ctx *) arg;
  if ((uptime > 60) && (ctx->nc != NULL)) {
    mg_send(ctx->nc, messtring, strlen(messtring));
  }
}

void connect_handler(struct mg_connection *c, int ev, void *evd,
                     void *user_data) {
  struct app_ctx *ctx = (struct app_ctx *) user_data;
  switch (ev) {
    case MG_EV_CONNECT:
      if ((*(int *) evd) != 0) {
        LOG(LL_ERROR, ("MG_EV_CONNECT error %d", *(int *) evd));
        ctx->nc = NULL;
      } else {
        LOG(LL_INFO, ("MG_EV_CONNECT success!"));
      }
      break;
    case MG_EV_CLOSE:
      LOG(LL_INFO, ("MG_EV_CLOSE - Connection closed"));
      ctx->nc = NULL;
      break;
  }
  (void) c;
  (void) evd;
}

static void init_application(struct app_ctx *ctx) {
  /*
   * in mos.yml
   * config_schema:
   *   - ["app.port1", "s", "tcp://192.168.xxx.yyy:1234", {}]
   */
  const char *srv = mgos_sys_config_get_app_port1();
  ctx->nc = mg_connect(mgos_get_mgr(), srv, connect_handler, ctx);
  LOG(LL_INFO, ("Connecting to %s (nc: %p)", srv, ctx->nc));
  ctx->timer_id = mgos_set_timer(5000, MGOS_TIMER_REPEAT, led_timer_cb, ctx);
}

static void wifi_cb(int ev, void *evd, void *arg) {
  static bool init = false;
  struct app_ctx *ctx = (struct app_ctx *) arg;
  switch (ev) {
    case MGOS_WIFI_EV_STA_IP_ACQUIRED:
    case MGOS_WIFI_EV_AP_STA_CONNECTED: {
      LOG(LL_INFO, ("%s", (ev == MGOS_WIFI_EV_STA_IP_ACQUIRED)
                              ? "MGOS_WIFI_EV_STA_IP_ACQUIRED"
                              : "MGOS_WIFI_EV_AP_STA_CONNECTED"));
      if (!init) {
        init = true;
        init_application(ctx);
      }
      break;
    }
    case MGOS_WIFI_EV_AP_STA_DISCONNECTED: {
      LOG(LL_INFO, ("WIFI_EV_AP_STA_DISCONNECTED"));
      init = false;
      if (ctx->timer_id != MGOS_INVALID_TIMER_ID) {
        mgos_clear_timer(ctx->timer_id);
        ctx->timer_id = MGOS_INVALID_TIMER_ID;
      }
      break;
    }
  }
  (void) evd;
}

enum mgos_app_init_result mgos_app_init(void) {
  struct app_ctx *ctx = (struct app_ctx *) calloc(1, sizeof(*ctx));
  mgos_event_add_group_handler(MGOS_WIFI_EV_BASE, wifi_cb, ctx);
  return MGOS_APP_INIT_SUCCESS;
}
1 Like
#6

Hi nliviu,

Thank you for your detailed example. I will follow this method.

JSW

#7

Last question on this topic… This example is really different from others provided on the Mongoose OS site. Is it possible to provide some additional information on how to receive and print a message from the server during a TCP connected session using the above code?

Thanks,

JSW

#8

Process the MG_EV_RECV event in connect_handler.

#9

Hi nliviu,

Obvious now. Thanks.

JSW