-
Notifications
You must be signed in to change notification settings - Fork 151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BG95 Support (IDFGH-14480) #739
Comments
Have you tested the BG95 with the modem library, initialized as a "GENERIC DEVICE"? I briefly checked the spec and think it should work as is, the BG95 is a drop in replacement for BG96 and is IMO even "closer" to the generic device than the bg96 (just some specific commands like reset or TCP socket API might be different) |
Hey @david-cermak, haven't tried that but will tonight and report back. Thanks! |
I was able to get a data connection going in CMUX mode it seems using the generic module! Thanks for that! I BG95: UART DTE created successfully
I BG95: Generic DCE created successfully
E BG95: Failed to set flow control
I BG95: CMUX mode set successfully
I BG95: Sync OK
I BG95: IMSI: [REDACTED]
I BG95: IMEI: [REDACTED]
I BG95: Module name: BG95-M3
I BG95: Firmware version: BG95M3LAR02A03
I BG95: ICCID: [REDACTED]
I BG95: Manufacturer: Quectel
I BG95: Model: BG95-M3
I BG95: Signal quality: RSSI=99, BER=99
I BG95: Connecting to cellular network this could take up to 240 seconds
I esp-netif_lwip-ppp: Connection lost
I BG95: IP event received
I BG95: Event ID: 7
I BG95: Modem Disconnect from PPP Server
I BG95: Attempting to recover connection...
One weird thing I am seeing though is that sometimes if I had a connection previously or power cycle etc the device will almost immediately just say disconnected and send me a disconnect ppp event. Any ideas on this @david-cermak |
Ok I have made more progress on this. It seems like I have now setup CMUX correctly and set the URC handler so that I get the URCs after entering CMUX mode. But I still have this weird ppp issue....basically what I'm trying to accomplish is the following.
Do I need to be handling the multiple terminals myself? I'm a little lost at this point and haven't been able to find any example that shows exactly what I want here unless I'm misunderstanding something? Any help would be greatly appreciated. @david-cermak . Also important to note that if my module gets its network up and then I restart the program it will work for connecting to PPP. |
Heres my current code also if this is helpful static const char *TAG = "BG95";
void BG95::on_ppp_changed(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data){
ESP_LOGI(TAG, "PPP changed event");
BG95* bg95 = static_cast<BG95*>(arg);
if (event_id == NETIF_PPP_ERRORUSER) {
if (bg95->callbacks) {
bg95->callbacks->cellularErrorOccured(bg95);
}
xEventGroupSetBits(bg95->event_group, ERROR_BIT);
}
}
void BG95::on_ip_event(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data){
BG95* bg95 = static_cast<BG95*>(arg);
ESP_LOGI(TAG, "IP event recieved");
ESP_LOGI(TAG, "Event ID: %d", event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
esp_netif_t *netif = event->esp_netif;
ESP_LOGI(TAG, "Modem Connect to PPP Server");
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask));
ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw));
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info);
ESP_LOGI(TAG, "Name Server1: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_info);
ESP_LOGI(TAG, "Name Server2: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
xEventGroupSetBits(bg95->event_group, CONNECT_BIT);
if (bg95->callbacks) {
bg95->callbacks->cellularNetworkConnected(bg95);
}
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
// xEventGroupSetBits(bg95->event_group, DISCONNECT_BIT);
//try to recover the connection
ESP_LOGI(TAG, "Attempting to recover connection...");
if (bg95->callbacks) {
bg95->callbacks->cellularNetworkDisconnected(bg95);
}
} else if (event_id == IP_EVENT_GOT_IP6) {
ESP_LOGI(TAG, "GOT IPv6 event!");
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
ESP_LOGI(TAG, "Got IPv6 address " IPV6STR, IPV62STR(event->ip6_info.ip));
}
}
//read callback
bool BG95::read_callback(uint8_t *data, size_t len){
ESP_LOGI(TAG, "Read callback called");
//log the data
ESP_LOGI(TAG, "Read data: %s", data);
return true;
}
BG95::BG95() {
ESP_LOGI(TAG, "Constructor called");
// Initialize event group
event_group = xEventGroupCreate();
// Power up first before any other initialization
gpio_config_t power_up_pin_config = {
.pin_bit_mask = (1ULL << BG95_PWK_PIN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&power_up_pin_config);
powerUp();
ESP_LOGI(TAG, "BG95 finished power up sequence");
vTaskDelay(pdMS_TO_TICKS(2000)); // Give module time to stabilize
// Initialize ESP-NETIF after power up
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* Configure the PPP netif */
esp_modem_dce_config dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("soracom.io");
esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP();
esp_netif = esp_netif_new(&netif_ppp_config);
assert(esp_netif);
// Configure the DTE
esp_modem_dte_config dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
dte_config.uart_config.port_num = UART_NUM_1;
dte_config.uart_config.tx_io_num = BG95_TX_PIN;
dte_config.uart_config.rx_io_num = BG95_RX_PIN;
dte_config.uart_config.rts_io_num = BG95_RTS_PIN;
dte_config.uart_config.cts_io_num = BG95_CTS_PIN;
dte_config.uart_config.flow_control = ESP_MODEM_FLOW_CONTROL_HW;
dte_config.uart_config.rx_buffer_size = 1024;
dte_config.uart_config.tx_buffer_size = 512;
dte_config.uart_config.event_queue_size = 30;
dte_config.uart_config.baud_rate = 115200;
dte_config.task_stack_size = 4096;
dte_config.task_priority = 5;
dte_config.dte_buffer_size = 512;
ESP_LOGI(TAG, "Initializing esp_modem for the BG95 module...");
// Create DTE first
dte = esp_modem::create_uart_dte(&dte_config);
if (!dte) {
ESP_LOGE(TAG, "Failed to create UART DTE");
return;
}
ESP_LOGI(TAG, "UART DTE created successfully");
vTaskDelay(pdMS_TO_TICKS(1000));
// Create DCE using the DTE
//dce = esp_modem::create_generic_dce(&dce_config, dte, esp_netif);
dce = create_BG95_GNSS_dce(&dce_config, dte, esp_netif);
if (!dce) {
ESP_LOGE(TAG, "Failed to create generic DCE");
return;
}
ESP_LOGI(TAG, "Generic DCE created successfully");
vTaskDelay(pdMS_TO_TICKS(1000));
//stop the ppp connection
// Now that DTE/DCE are initialized, register for events
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &on_ip_event, this));
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, this));
// Set the read callback BEFORE setting CMUX mode
dte->set_read_cb(read_callback);
// Set flow control if needed
if (dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
auto result = dce->set_flow_control(0, 0);
if (result != esp_modem::command_result::OK) {
ESP_LOGE(TAG, "Failed to set flow control");
} else {
ESP_LOGI(TAG, "HW flow control OK");
}
}
vTaskDelay(pdMS_TO_TICKS(1000));
// Initial sync and basic AT commands
auto sync_result = dce->sync();
if(sync_result != esp_modem::command_result::OK) {
ESP_LOGE(TAG, "Failed to sync");
} else {
ESP_LOGI(TAG, "Sync OK");
}
vTaskDelay(pdMS_TO_TICKS(1000));
// Check network registration before setting CMUX mode
std::string response;
if (dce->at("AT+CREG?", response, 1000) == esp_modem::command_result::OK) {
ESP_LOGI(TAG, "Network registration status: %s", response.c_str());
}
vTaskDelay(pdMS_TO_TICKS(1000));
// Only set CMUX mode after all direct UART operations are complete
bool cmux_mode_set = dce->set_mode(esp_modem::modem_mode::CMUX_MODE);
if(!cmux_mode_set) {
ESP_LOGE(TAG, "Failed to set CMUX mode");
} else {
ESP_LOGI(TAG, "CMUX mode set successfully");
}
vTaskDelay(pdMS_TO_TICKS(2000));
//set the read callbacks again
dce->set_urc([this](uint8_t *data, size_t len) {
return this->urc_callback(data, len);
});
// Rest of the initialization (getting IMSI, IMEI, etc.)
//Get basic module information
std::string imsi;
if (dce->get_imsi(imsi) != esp_modem::command_result::OK) {
ESP_LOGE(TAG, "Failed to get IMSI are you sure the module has a sim card?");
} else {
ESP_LOGI(TAG, "IMSI: %s", imsi.c_str());
this->imsi = imsi;
}
std::string imei;
if (dce->get_imei(imei) != esp_modem::command_result::OK) {
ESP_LOGE(TAG, "Failed to get IMEI");
} else {
ESP_LOGI(TAG, "IMEI: %s", imei.c_str());
this->imei = imei;
}
std::string module_name;
if (dce->get_module_name(module_name) != esp_modem::command_result::OK) {
ESP_LOGE(TAG, "Failed to get module name");
} else {
ESP_LOGI(TAG, "Module name: %s", module_name.c_str());
this->modelString = module_name;
}
// Get firmware version using AT+CGMR
if (dce->at("AT+CGMR", response, 1000) == esp_modem::command_result::OK) {
ESP_LOGI(TAG, "Firmware version: %s", response.c_str());
this->firmwareVersion = response;
} else {
ESP_LOGE(TAG, "Failed to get firmware version");
}
// Get ICCID using AT+QCCID
if (dce->at("AT+QCCID", response, 1000) == esp_modem::command_result::OK) {
ESP_LOGI(TAG, "ICCID: %s", response.c_str());
this->iccid = response;
} else {
ESP_LOGE(TAG, "Failed to get ICCID");
}
// Get manufacturer identification using AT+CGMI
if (dce->at("AT+CGMI", response, 1000) == esp_modem::command_result::OK) {
ESP_LOGI(TAG, "Manufacturer: %s", response.c_str());
// Store in a new member variable if needed
} else {
ESP_LOGE(TAG, "Failed to get manufacturer identification");
}
// Get model identification using AT+CGMM
if (dce->at("AT+CGMM", response, 1000) == esp_modem::command_result::OK) {
ESP_LOGI(TAG, "Model: %s", response.c_str());
// Store in a new member variable if needed
} else {
ESP_LOGE(TAG, "Failed to get model identification");
}
//get the signal quality
int rssi, ber;
auto result = dce->get_signal_quality(rssi, ber);
if (result == esp_modem::command_result::OK) {
ESP_LOGI(TAG, "Signal quality: RSSI=%d, BER=%d", rssi, ber);
} else {
ESP_LOGE(TAG, "Failed to get signal quality");
}
//get the network attachement state
int attachment_state = 0;
auto resultNetworkAttachment = dce->get_network_attachment_state(attachment_state);
if(resultNetworkAttachment != esp_modem::command_result::OK){
ESP_LOGE(TAG, "Failed to get network attachement state");
} else {
ESP_LOGI(TAG, "Network attachement state: %d", attachment_state);
}
std::string responseNetworkRegistration;
ESP_LOGI(TAG, "Enabling network status URCs...");
// Enable Circuit Switched network registration URC with location info
if (dce->at("AT+CREG=2", responseNetworkRegistration, 1000) == esp_modem::command_result::OK) {
ESP_LOGI(TAG, "Circuit Switched network registration URCs enabled");
} else {
ESP_LOGE(TAG, "Failed to enable CREG URCs");
}
// Enable GPRS network registration URC with location info
if (dce->at("AT+CGREG=2", responseNetworkRegistration, 1000) == esp_modem::command_result::OK) {
ESP_LOGI(TAG, "GPRS network registration URCs enabled");
} else {
ESP_LOGE(TAG, "Failed to enable CGREG URCs");
}
//Enable AT+CEREG which is EPS Network Registration Status
if (dce->at("AT+CEREG=2", responseNetworkRegistration, 1000) == esp_modem::command_result::OK) {
ESP_LOGI(TAG, "EPS Network Registration Status enabled");
} else {
ESP_LOGE(TAG, "Failed to enable EPS Network Registration Status");
}
// Enable GPRS event reporting
if (dce->at("AT+CGEREP=2,1", responseNetworkRegistration, 1000) == esp_modem::command_result::OK) {
ESP_LOGI(TAG, "GPRS event reporting enabled");
} else {
ESP_LOGE(TAG, "Failed to enable GPRS event reporting");
}
}
BG95::~BG95() {
if (esp_netif) {
esp_netif_destroy(esp_netif);
}
if (event_group) {
vEventGroupDelete(event_group);
}
}
bool BG95::connect() {
ESP_LOGI(TAG, "Connecting to cellular network this could take up to %d seconds", MAX_CONNECT_TIMEOUT/1000);
//esp_modem::command_result result = dce->set_data_mode();
//if (result != esp_modem::command_result::OK) {
// ESP_LOGE(TAG, "Failed to set data mode with error code %d", result);
// if (callbacks) {
// callbacks->cellularNetworkFailedToConnect(this);
// }
// return false;
//}
// Wait for connection event
EventBits_t bits = xEventGroupWaitBits(event_group,
CONNECT_BIT | ERROR_BIT,
pdFALSE,
pdFALSE,
pdMS_TO_TICKS(MAX_CONNECT_TIMEOUT));
// Handle timeout case
if (bits == 0) {
ESP_LOGW(TAG, "Connection attempt timed out after %d seconds", MAX_CONNECT_TIMEOUT/1000);
if (callbacks) {
callbacks->cellularNetworkFailedToConnect(this);
}
return false;
}
// Handle error case
if (bits & ERROR_BIT) {
ESP_LOGW(TAG, "Connection attempt failed with error");
return false;
}
return (bits & CONNECT_BIT) != 0;
}
bool BG95::disconnect() {
esp_modem::command_result result = dce->set_command_mode();
if (result != esp_modem::command_result::OK) {
return false;
}
return true;
}
bool BG95::isConnected() {
return (xEventGroupGetBits(event_group) & CONNECT_BIT) != 0;
}
int BG95::getSignalQuality() {
int rssi, ber;
auto result = dce->get_signal_quality(rssi, ber);
if (result == esp_modem::command_result::OK) {
if (callbacks) {
callbacks->cellularSignalQualityChanged(this, rssi);
}
return rssi;
}
return -1;
}
void BG95::powerUp(){
ESP_LOGI(TAG, "Powering up the BG96 module...");
// this needs to be low for at least 500 ms to power up the module after this happens After STATUS pin outputs
// a high voltage level, PWRKEY pin can be released.
// gpio_set_level(GPIO_NUM_10, 0);
// vTaskDelay(550 / portTICK_PERIOD_MS);
//gpio_set_level(GPIO_NUM_10, 1);
//set the power up pin to high this is specific to this dev board - this is fucking retarted you have to connect the 5v and the 3.3v to the module
//also this is not what the BG96 datasheet says to do
gpio_set_level(GPIO_NUM_10, 1);
}
void BG95::powerDown(){
// Drive PWRKEY low at least 650 ms and then release it, so that the module will execute power-down
// procedure. The power-down scenario is illustrated in the following figure.
ESP_LOGI(TAG, "Powering down the BG96 module...");
gpio_set_level(GPIO_NUM_10, 0);
vTaskDelay(1000 / portTICK_PERIOD_MS);
gpio_set_level(GPIO_NUM_10, 1);
}
//we can handle the urcs here
esp_modem::command_result BG95::urc_callback(uint8_t *data, size_t len) {
ESP_LOGI(TAG, "URC received: %.*s", len, data);
if(strstr((char*)data, "+CREG:") != NULL){
ESP_LOGI(TAG, "Network registration status: %s", data);
//set the connected bit
xEventGroupSetBits(event_group, CONNECT_BIT);
//try to ping
this->ping("google.com", 3, 1000, 5000);
}
return esp_modem::command_result::OK;
}
bool BG95::ping(const char* host, int count, int interval_ms, int timeout_ms) {
//delay 5 seconds
vTaskDelay(pdMS_TO_TICKS(5000));
if (!isConnected()) {
ESP_LOGE(TAG, "Cannot ping: not connected to network");
return false;
}
// Configure ping parameters
ip_addr_t target_addr;
esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
// Resolve hostname to IP
struct hostent *he = lwip_gethostbyname(host);
if (he == NULL) {
ESP_LOGE(TAG, "DNS lookup failed for %s", host);
return false;
}
// Copy IP address from hostent to ip_addr_t
memcpy(&target_addr, he->h_addr, sizeof(ip_addr_t));
ping_config.target_addr = target_addr;
ping_config.count = count;
ping_config.interval_ms = interval_ms;
ping_config.timeout_ms = timeout_ms;
ping_config.task_stack_size = 4096;
ping_config.task_prio = 2;
// Ping callbacks
esp_ping_callbacks_t cbs = {
.on_ping_success = [](esp_ping_handle_t hdl, void *args) {
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
ESP_LOGI(TAG, "Ping success: seq=%d ttl=%d time=%lu ms", seqno, ttl, elapsed_time);
},
.on_ping_timeout = [](esp_ping_handle_t hdl, void *args) {
uint16_t seqno;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
ESP_LOGI(TAG, "Ping timeout: seq=%d", seqno);
},
.on_ping_end = [](esp_ping_handle_t hdl, void *args) {
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
ESP_LOGI(TAG, "Ping statistics: %lu packets transmitted, %lu received, time %lu ms",
transmitted, received, total_time_ms);
}
};
esp_ping_handle_t ping;
ESP_ERROR_CHECK(esp_ping_new_session(&ping_config, &cbs, &ping));
ESP_ERROR_CHECK(esp_ping_start(ping));
return true;
}
//method used to request gnss data from the module
bool BG95::requestGnssData() {
//first turn on the gnss power with AT+QGPS
std::string response;
if (dce->at("AT+QGPS=1", response, 1000) == esp_modem::command_result::OK) {
ESP_LOGI(TAG, "GNSS power turned on");
} else {
ESP_LOGE(TAG, "Failed to turn on GNSS power");
//return false;
}
//then request the data AT+QGPSLOC
if (dce->at("AT+QGPSLOC", response, 1000) == esp_modem::command_result::OK) {
ESP_LOGI(TAG, "GNSS data requested");
} else {
ESP_LOGE(TAG, "Failed to request GNSS data");
//return false;
}
return true;
}
|
Hey @david-cermak any support on this?
Hey @david-cermak any support on this? |
I think the problem is in here: esp_modem::command_result BG95::urc_callback(uint8_t *data, size_t len) {
ESP_LOGI(TAG, "URC received: %.*s", len, data);
if(strstr((char*)data, "+CREG:") != NULL){
ESP_LOGI(TAG, "Network registration status: %s", data);
//set the connected bit
xEventGroupSetBits(event_group, CONNECT_BIT);
//try to ping
this->ping("google.com", 3, 1000, 5000);
}
return esp_modem::command_result::OK;
} this is the URC callback, which is called directly from the modem thread, and you're actually waiting 5 seconds if I'm not mistaken stalling the modem service, correct? Suggest leaving the event group signal in the callback xEventGroupSetBits(event_group, CONNECT_BIT); and moving the ping method to be called upon the signal notification. |
Is your feature request related to a problem?
I see there is B96 support could we have the BG95 added also? Thanks
Describe the solution you'd like.
No response
Describe alternatives you've considered.
No response
Additional context.
No response
The text was updated successfully, but these errors were encountered: