#include #include #include #include #include #include #include #include #include static char device_id[NRF_CLOUD_CLIENT_ID_MAX_LEN + 1]; LOG_MODULE_REGISTER(main, LOG_LEVEL_INF); K_SEM_DEFINE(lte_connected, 0, 1); /* Sensor devices */ const struct device *bme280 = DEVICE_DT_GET_ANY(bosch_bme280); const struct device *apds = DEVICE_DT_GET_ANY(avago_apds9960); static void lte_handler(const struct lte_lc_evt *const evt) { switch (evt->type) { case LTE_LC_EVT_NW_REG_STATUS: LOG_INF("Network registration status: %d", evt->nw_reg_status); if (evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME || evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_ROAMING) { LOG_INF("Connected to %s network", evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ? "home" : "roaming"); k_sem_give(<e_connected); } else if (evt->nw_reg_status == LTE_LC_NW_REG_NOT_REGISTERED || evt->nw_reg_status == LTE_LC_NW_REG_SEARCHING) { LOG_INF("Network not registered or searching..."); } else { LOG_ERR("Unexpected registration status: %d", evt->nw_reg_status); } break; case LTE_LC_EVT_CELL_UPDATE: LOG_INF("Cell update: Cell ID %d", evt->cell.id); break; case LTE_LC_EVT_LTE_MODE_UPDATE: LOG_INF("LTE mode update: %d", evt->lte_mode); break; case LTE_LC_EVT_MODEM_EVENT: LOG_INF("Modem event: %d", evt->modem_evt); break; default: LOG_INF("Unhandled LTE event: %d", evt->type); break; } } static int connect_to_lte(void) { int err; LOG_INF("Waiting for network..."); k_sem_reset(<e_connected); // Reset the semaphore to 0 err = lte_lc_connect_async(lte_handler); if (err) { LOG_ERR("Failed to init modem, error: %d", err); return err; } k_sem_take(<e_connected, K_FOREVER); // Wait for LTE connection LOG_INF("Connected to LTE"); return 0; } static int init(void) { int err; err = nrf_modem_lib_init(); if (err) { LOG_ERR("Failed to initialize modem library: 0x%X", err); return -EFAULT; } LOG_INF("Modem library initialized"); return 0; } static int setup_connection(void) { int err; /* Connect to LTE */ err = connect_to_lte(); if (err) { LOG_ERR("Failed to connect to cellular network: %d", err); return err; } /* Get the device ID */ memset(device_id, 0, sizeof(device_id)); err = nrf_cloud_client_id_get(device_id, sizeof(device_id)); if (err) { LOG_ERR("Failed to get device ID, error: %d", err); return err; } size_t id_len = strlen(device_id); if (id_len == 0 || id_len >= NRF_CLOUD_CLIENT_ID_MAX_LEN) { LOG_ERR("Invalid client ID length: %d (max: %d)", id_len, NRF_CLOUD_CLIENT_ID_MAX_LEN); return -EINVAL; } LOG_INF("Device ID: %s (length: %d)", device_id, strlen(device_id)); LOG_INF("Attempting initial nRF Cloud connection..."); struct nrf_cloud_init_param params = { .client_id = device_id, // Use the device ID retrieved earlier .event_handler = NULL, // Add a handler if you need to process cloud events }; if (strlen(device_id) > NRF_CLOUD_CLIENT_ID_MAX_LEN) { LOG_ERR("Device ID too long: %d > %d", strlen(device_id), NRF_CLOUD_CLIENT_ID_MAX_LEN); return -EINVAL; } err = nrf_cloud_init(¶ms); if (err) { LOG_ERR("Failed to initialize nRF Cloud library: %d", err); switch (err) { case -EINVAL: LOG_ERR("Invalid argument - check client ID or Kconfig"); break; case -EACCES: LOG_ERR("Access denied - check credentials"); break; case -EFAULT: LOG_ERR("Internal error - check modem initialization"); break; default: LOG_ERR("Unknown error"); break; } return err; } LOG_INF("Attempting initial nRF Cloud connection..."); err = nrf_cloud_connect(); if (err) { LOG_ERR("Initial nRF Cloud connection failed: %d", err); return err; } LOG_INF("Connected to nRF Cloud"); return 0; } static int setup(void) { int err; /* Initialize libraries and hardware */ err = init(); if (err) { LOG_ERR("Initialization failed."); return err; } /* Initiate Connection */ err = setup_connection(); if (err) { LOG_ERR("Connection set-up failed."); return err; } return 0; } static int read_sensors(struct sensor_value *temp, struct sensor_value *hum, struct sensor_value *light) { if (!device_is_ready(bme280) || !device_is_ready(apds)) { LOG_ERR("Sensors not ready!"); return -ENODEV; } /* Fetch and read temperature & humidity from BME280 */ sensor_sample_fetch(bme280); sensor_channel_get(bme280, SENSOR_CHAN_AMBIENT_TEMP, temp); sensor_channel_get(bme280, SENSOR_CHAN_HUMIDITY, hum); /* Fetch and read light level from APDS-9960 */ sensor_sample_fetch(apds); sensor_channel_get(apds, SENSOR_CHAN_LIGHT, light); return 0; } static int send_data_to_cloud(struct sensor_value *temp, struct sensor_value *hum, struct sensor_value *light) { char json_payload[128]; int err; if (nrf_cloud_disconnect()) { LOG_ERR("Not connected to nRF Cloud, attempting reconnect..."); err = nrf_cloud_connect(); if (err) { LOG_ERR("Reconnect failed: %d", err); switch (err) { case -ECONNREFUSED: LOG_ERR("Connection refused - check credentials"); break; case -ETIMEDOUT: LOG_ERR("Connection timed out - check network"); break; default: LOG_ERR("Unknown error"); break; } return err; } k_sleep(K_SECONDS(5)); // Give it time to connect } /* Create JSON payload */ snprintf(json_payload, sizeof(json_payload), "{\"temperature\":%d.%06d, \"humidity\":%d.%06d, \"light\":%d.%06d}", temp->val1, temp->val2, hum->val1, hum->val2, light->val1, light->val2); /* Send JSON to nRF Cloud */ struct nrf_cloud_tx_data msg = { .data.ptr = json_payload, .data.len = strlen(json_payload), .qos = MQTT_QOS_1_AT_LEAST_ONCE, .topic_type = NRF_CLOUD_TOPIC_MESSAGE }; LOG_INF("Attempting to send data: %s", json_payload); err = nrf_cloud_send(&msg); if (err) { LOG_ERR("Failed to send data to nRF Cloud: %d", err); if (err == -EACCES) { LOG_ERR("Permission denied - check MQTT connection or credentials"); } return err; } LOG_INF("Successfully sent data to nRF Cloud"); return 0; } static bool cred_check(struct nrf_cloud_credentials_status *const cs) { int ret; ret = nrf_cloud_credentials_check(cs); if (ret) { LOG_ERR("nRF Cloud credentials check failed, error: %d", ret); return false; } /* Since this is a REST sample, we only need two credentials: * - a CA for the TLS connections * - a private key to sign the JWT */ LOG_INF("Checking credentials in sec tag %u", cs->sec_tag); if (!cs->ca) { LOG_WRN("CA certificate missing"); } if (!cs->ca_aws) { LOG_WRN("AWS CA certificate missing"); } if (!cs->prv_key) { LOG_WRN("Private key missing"); } // For MQTT, you typically need CA and private key at minimum return (cs->ca && cs->prv_key); } int main(void) { struct nrf_cloud_credentials_status cs; int err; err = setup(); if (err) { LOG_ERR("Setup failed, stopping."); return err; } if (!cred_check(&cs)) { LOG_ERR("Credentials check failed"); } struct sensor_value temp, hum, light; LOG_INF("Starting nRF9161 Sensor Cloud Application"); if (!device_is_ready(bme280)) { printk("BME280 device is not ready\n"); } if (!device_is_ready(apds)) { printk("APDS9960 device is not ready\n"); } while (1) { /* Read sensor values */ if (read_sensors(&temp, &hum, &light) == 0) { LOG_INF("Temp: %d.%06d°C, Humidity: %d.%06d%%, Light: %d.%06d lux", temp.val1, temp.val2, hum.val1, hum.val2, light.val1, light.val2); /* Send data to nRF Cloud */ send_data_to_cloud(&temp, &hum, &light); } /* Send data every 60 seconds */ k_sleep(K_SECONDS(10)); } return 0; }