From 7e6cd280e58bae5c32a511b34cac1fcd40aa001f Mon Sep 17 00:00:00 2001 From: iwahdan88 Date: Tue, 30 Jul 2019 23:06:17 +0200 Subject: [PATCH 01/15] [PYFW-86] Fix wlan sta interface terminating after lte disconnect --- esp32/get_idf_libs.py | 1 - esp32/lte/lteppp.c | 16 ++++++++++++++++ esp32/lte/lteppp.h | 2 ++ esp32/mods/modlte.c | 8 +++++++- esp32/mods/modnetwork.c | 6 ++---- esp32/mods/modnetwork.h | 2 +- esp32/mods/modwlan.c | 19 +++++++++++++------ 7 files changed, 41 insertions(+), 13 deletions(-) diff --git a/esp32/get_idf_libs.py b/esp32/get_idf_libs.py index 79f2d852ae..4be0d11f74 100644 --- a/esp32/get_idf_libs.py +++ b/esp32/get_idf_libs.py @@ -59,7 +59,6 @@ def main(): shutil.copy(src + '/vfs/libvfs.a', dsttmpapp) shutil.copy(src + '/wpa_supplicant/libwpa_supplicant.a', dsttmpapp) shutil.copy(src + '/xtensa-debug-module/libxtensa-debug-module.a', dsttmpapp) - shutil.copy(src + '/openthread/libopenthread.a', dsttmpapp) except: print("Couldn't Copy IDF libs defaulting to Local Lib Folders!") traceback.print_exc() diff --git a/esp32/lte/lteppp.c b/esp32/lte/lteppp.c index db3408ba83..2e0523d941 100644 --- a/esp32/lte/lteppp.c +++ b/esp32/lte/lteppp.c @@ -27,6 +27,7 @@ #include "pins.h" #include "mpsleep.h" #include "esp32_mphal.h" +#include "lwip/dns.h" /****************************************************************************** DEFINE CONSTANTS @@ -78,6 +79,8 @@ static bool ltepp_ppp_conn_up = false; static ltepppconnstatus_t lteppp_connstatus = LTE_PPP_IDLE; +static ip_addr_t ltepp_dns_info[2]={0}; + /****************************************************************************** DECLARE PRIVATE FUNCTIONS ******************************************************************************/ @@ -188,6 +191,14 @@ void lteppp_set_state(lte_state_t state) { xSemaphoreGive(xLTESem); } +void lteppp_set_default_inf(void) +{ + pppapi_set_default(lteppp_pcb); + //Restore DNS + dns_setserver(0, &(ltepp_dns_info[0])); + dns_setserver(1, &(ltepp_dns_info[1])); +} + void lteppp_set_legacy(lte_legacy_t legacy) { xSemaphoreTake(xLTESem, portMAX_DELAY); lteppp_lte_legacy = legacy; @@ -656,6 +667,11 @@ static void lteppp_status_cb (ppp_pcb *pcb, int err_code, void *ctx) { lte_gw = pppif->gw.u_addr.ip4.addr; lte_netmask = pppif->netmask.u_addr.ip4.addr; lte_ipv4addr = pppif->ip_addr.u_addr.ip4.addr; + if(lte_ipv4addr > 0) + { + ltepp_dns_info[0] = dns_getserver(0); + ltepp_dns_info[1] = dns_getserver(1); + } // printf("ipaddr = %s\n", ipaddr_ntoa(&pppif->ip_addr)); // printf("gateway = %s\n", ipaddr_ntoa(&pppif->gw)); // printf("netmask = %s\n", ipaddr_ntoa(&pppif->netmask)); diff --git a/esp32/lte/lteppp.h b/esp32/lte/lteppp.h index b0716ee5a3..9977aa9765 100644 --- a/esp32/lte/lteppp.h +++ b/esp32/lte/lteppp.h @@ -124,6 +124,8 @@ extern bool ltepp_is_ppp_conn_up(void); extern void lteppp_suspend(void); extern void lteppp_resume(void); + +extern void lteppp_set_default_inf(void); #ifdef LTE_DEBUG_BUFF extern char* lteppp_get_log_buff(void); #endif diff --git a/esp32/mods/modlte.c b/esp32/mods/modlte.c index 460c52480c..f171b8afb6 100644 --- a/esp32/mods/modlte.c +++ b/esp32/mods/modlte.c @@ -128,7 +128,7 @@ STATIC mp_obj_t lte_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t STATIC mp_obj_t lte_deinit(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); STATIC mp_obj_t lte_disconnect(mp_obj_t self_in); - +static void lte_set_default_inf(void); /****************************************************************************** DEFINE PUBLIC FUNCTIONS ******************************************************************************/ @@ -322,6 +322,11 @@ static void lte_check_init(void) { } } +static void lte_set_default_inf(void) +{ + lteppp_set_default_inf(); +} + static void lte_check_inppp(void) { if (lteppp_get_state() == E_LTE_PPP) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "LTE modem is in data state, cannot send AT commands")); @@ -1385,4 +1390,5 @@ const mod_network_nic_type_t mod_network_nic_type_lte = { .n_ioctl = lwipsocket_socket_ioctl, .n_setupssl = lwipsocket_socket_setup_ssl, .inf_up = ltepp_is_ppp_conn_up, + .set_default_inf = lte_set_default_inf }; diff --git a/esp32/mods/modnetwork.c b/esp32/mods/modnetwork.c index 8cb0e4592d..b53cfd720c 100644 --- a/esp32/mods/modnetwork.c +++ b/esp32/mods/modnetwork.c @@ -94,13 +94,11 @@ void mod_network_deregister_nic(mp_obj_t nic) { mp_obj_t nic_rem = MP_STATE_PORT(mod_network_nic_list).items[i]; if(mp_obj_get_type(nic_rem) == (mp_obj_type_t *)&mod_network_nic_type_wlan) { - mod_network_nic_type_wlan.set_inf_up(); + mod_network_nic_type_wlan.set_default_inf(); } else if (mp_obj_get_type(nic_rem) == (mp_obj_type_t *)&mod_network_nic_type_lte) { - if (mod_network_nic_type_lte.set_inf_up != NULL) { - mod_network_nic_type_lte.set_inf_up(); - } + mod_network_nic_type_lte.set_default_inf(); } } #endif diff --git a/esp32/mods/modnetwork.h b/esp32/mods/modnetwork.h index 8f0907984c..bc3ca93714 100644 --- a/esp32/mods/modnetwork.h +++ b/esp32/mods/modnetwork.h @@ -84,7 +84,7 @@ typedef struct _mod_network_nic_type_t { // Interface status bool (*inf_up)(void); // Bring Inf_up - void (*set_inf_up)(void); + void (*set_default_inf)(void); } mod_network_nic_type_t; typedef struct _mod_network_socket_base_t { diff --git a/esp32/mods/modwlan.c b/esp32/mods/modwlan.c index 27c89a7560..61552c25b8 100644 --- a/esp32/mods/modwlan.c +++ b/esp32/mods/modwlan.c @@ -124,6 +124,10 @@ static bool is_inf_up = false; //mutex for Timeout Counter protection SemaphoreHandle_t timeout_mutex; +#if defined(FIPY) || defined(GPY) +// Variable saving DNS info +static tcpip_adapter_dns_info_t wlan_sta_inf_dns_info; +#endif /****************************************************************************** DECLARE PUBLIC DATA @@ -157,7 +161,7 @@ static void wlan_timer_callback( TimerHandle_t xTimer ); static void wlan_validate_country(const char * country); static void wlan_validate_country_policy(uint8_t policy); STATIC void wlan_stop_sta_conn_timer(); -STATIC void wlan_inf_up(void); +STATIC void wlan_set_default_inf(void); //***************************************************************************** // //! \brief The Function Handles WLAN Events @@ -347,7 +351,11 @@ STATIC esp_err_t wlan_event_handler(void *ctx, system_event_t *event) { break; case SYSTEM_EVENT_STA_GOT_IP: /**< ESP32 station got IP from connected AP */ xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); - is_inf_up = true; +#if defined(FIPY) || defined(GPY) + // Save DNS info for restoring if wifi inf is usable again after LTE disconnect + tcpip_adapter_get_dns_info(TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_DNS_MAIN, &wlan_sta_inf_dns_info); +#endif + is_inf_up = true; break; case SYSTEM_EVENT_STA_DISCONNECTED: /**< ESP32 station disconnected from AP */ xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); @@ -943,12 +951,11 @@ STATIC void promiscuous_callback(void *buf, wifi_promiscuous_pkt_type_t type) } } -STATIC void wlan_inf_up(void) +STATIC void wlan_set_default_inf(void) { if (wlan_obj.mode == WIFI_MODE_STA || wlan_obj.mode == WIFI_MODE_APSTA) { + tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_DNS_MAIN, &wlan_sta_inf_dns_info); tcpip_adapter_up(TCPIP_ADAPTER_IF_STA); - tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); - tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); } } @@ -2619,7 +2626,7 @@ const mod_network_nic_type_t mod_network_nic_type_wlan = { .n_ioctl = lwipsocket_socket_ioctl, .n_setupssl = lwipsocket_socket_setup_ssl, .inf_up = wlan_is_inf_up, - .set_inf_up = wlan_inf_up + .set_default_inf = wlan_set_default_inf }; //STATIC const mp_irq_methods_t wlan_irq_methods = { From 4523098dff42e53ba9f4092c0d480f986c284cd3 Mon Sep 17 00:00:00 2001 From: iwahdan88 Date: Tue, 30 Jul 2019 23:09:27 +0200 Subject: [PATCH 02/15] [v1.20.0.rc12.1] Hot fix Fix wlan sta interface terminating after lte disconnect --- esp32/pycom_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp32/pycom_version.h b/esp32/pycom_version.h index 93077042f5..c13efbe5fa 100644 --- a/esp32/pycom_version.h +++ b/esp32/pycom_version.h @@ -10,7 +10,7 @@ #ifndef VERSION_H_ #define VERSION_H_ -#define SW_VERSION_NUMBER "1.20.0.rc12" +#define SW_VERSION_NUMBER "1.20.0.rc12.1" #define LORAWAN_VERSION_NUMBER "1.0.2" From 76d6e8413b4297f622a80933bd0691585534433f Mon Sep 17 00:00:00 2001 From: iwahdan88 Date: Tue, 30 Jul 2019 23:44:56 +0200 Subject: [PATCH 03/15] Fix compilation error --- esp32/mods/modwlan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esp32/mods/modwlan.c b/esp32/mods/modwlan.c index 61552c25b8..8179d276ac 100644 --- a/esp32/mods/modwlan.c +++ b/esp32/mods/modwlan.c @@ -953,10 +953,12 @@ STATIC void promiscuous_callback(void *buf, wifi_promiscuous_pkt_type_t type) STATIC void wlan_set_default_inf(void) { +#if defined(FIPY) || defined(GPY) if (wlan_obj.mode == WIFI_MODE_STA || wlan_obj.mode == WIFI_MODE_APSTA) { tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_DNS_MAIN, &wlan_sta_inf_dns_info); tcpip_adapter_up(TCPIP_ADAPTER_IF_STA); } +#endif } //STATIC void wlan_get_sl_mac (void) { From 4922b9c6cd82ff48b551ab38ab9155d2bddf236b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9za=20Husi?= Date: Wed, 31 Jul 2019 19:52:35 +0200 Subject: [PATCH 04/15] PYFW-354: LittleFS causing random crash (#49) --- esp32/littlefs/vfs_littlefs.c | 50 ++++++++++++++++-------------- esp32/littlefs/vfs_littlefs.h | 2 +- esp32/littlefs/vfs_littlefs_file.c | 6 ++-- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/esp32/littlefs/vfs_littlefs.c b/esp32/littlefs/vfs_littlefs.c index 23526f43dc..d642e0fffa 100644 --- a/esp32/littlefs/vfs_littlefs.c +++ b/esp32/littlefs/vfs_littlefs.c @@ -309,6 +309,10 @@ int littlefs_open_common_helper(lfs_t *lfs, const char* file_path, lfs_file_t *f } } } + else { + // Problem happened, free up the attributes + littlefs_free_up_attributes(cfg); + } return res; } @@ -317,11 +321,14 @@ int littlefs_close_common_helper(lfs_t *lfs, lfs_file_t *fp, struct lfs_file_con // Update timestamp if it has been requested if(*timestamp_update_ptr == true) { - littlefs_update_timestamp_fp(fp); + littlefs_update_timestamp_cfg(cfg); + *timestamp_update_ptr = false; } int res = lfs_file_close(lfs, fp); - littlefs_free_up_attributes(cfg); + if(res == LFS_ERR_OK) { + littlefs_free_up_attributes(cfg); + } return res; } @@ -350,13 +357,15 @@ void littlefs_prepare_attributes(struct lfs_file_config *cfg) cfg->attrs = m_malloc(cfg->attr_count * sizeof(struct lfs_attr)); // Set attribute for storing the timestamp - cfg->attrs->size = sizeof(lfs_timestamp_attribute_t); - cfg->attrs->type = LFS_ATTRIBUTE_TIMESTAMP; - cfg->attrs->buffer = m_malloc(sizeof(lfs_timestamp_attribute_t)); + cfg->attrs[0].size = sizeof(lfs_timestamp_attribute_t); + cfg->attrs[0].type = LFS_ATTRIBUTE_TIMESTAMP; + cfg->attrs[0].buffer = m_malloc(sizeof(lfs_timestamp_attribute_t)); + } void littlefs_free_up_attributes(struct lfs_file_config *cfg) { + cfg->attr_count = 0; // Currently we only have 1 attribute for timestamp m_free(cfg->attrs[0].buffer); m_free(cfg->attrs); @@ -369,31 +378,26 @@ int littlefs_update_timestamp(lfs_t* lfs, const char* file_relative_path) lfs_timestamp_attribute_t ts; ts.fdate = (WORD)(tm >> 16); ts.ftime = (WORD)tm; + // Write directly the timestamp value onto the flash return lfs_setattr(lfs, file_relative_path, LFS_ATTRIBUTE_TIMESTAMP, &ts, sizeof(lfs_timestamp_attribute_t)); } -void littlefs_update_timestamp_fp(lfs_file_t* fp) +void littlefs_update_timestamp_cfg(struct lfs_file_config *cfg) { - DWORD tm = get_fattime(); - lfs_timestamp_attribute_t ts; - ts.fdate = (WORD)(tm >> 16); - ts.ftime = (WORD)tm; - - // Until we only have 1 attribute, it is good to write the 0th element - memcpy(fp->cfg->attrs[0].buffer, &ts, sizeof(ts)); -} - -lfs_timestamp_attribute_t littlefs_get_timestamp_fp(lfs_file_t* fp) -{ - lfs_timestamp_attribute_t ts; - - // Until we only have 1 attribute, it is good to read the 0th element - memcpy(&ts, fp->cfg->attrs[0].buffer, sizeof(ts)); - return ts; + // Check is needed to prevent any accidental case when cfg->attrs[x].buffer is already freed up by someone else + if(cfg->attr_count > 0) { + DWORD tm = get_fattime(); + lfs_timestamp_attribute_t ts; + ts.fdate = (WORD)(tm >> 16); + ts.ftime = (WORD)tm; + + // Until we only have 1 attribute, it is good to write the 0th element + // This will automatically written out to the flash when close is performed + memcpy(cfg->attrs[0].buffer, &ts, sizeof(ts)); + } } - typedef struct _mp_vfs_littlefs_ilistdir_it_t { mp_obj_base_t base; mp_fun_1_t iternext; diff --git a/esp32/littlefs/vfs_littlefs.h b/esp32/littlefs/vfs_littlefs.h index 36af99883a..48a1e98ed4 100644 --- a/esp32/littlefs/vfs_littlefs.h +++ b/esp32/littlefs/vfs_littlefs.h @@ -45,7 +45,7 @@ extern int littlefs_close_common_helper(lfs_t *lfs, lfs_file_t *fp, struct lfs_f extern int littlefs_stat_common_helper(lfs_t *lfs, const char* file_path, struct lfs_info *fno, lfs_timestamp_attribute_t *ts); extern void littlefs_free_up_attributes(struct lfs_file_config *cfg); extern int littlefs_update_timestamp(lfs_t* lfs, const char* file_relative_path); -extern void littlefs_update_timestamp_fp(lfs_file_t* fp); +extern void littlefs_update_timestamp_cfg(struct lfs_file_config *cfg); extern lfs_timestamp_attribute_t littlefs_get_timestamp_fp(lfs_file_t* fp); diff --git a/esp32/littlefs/vfs_littlefs_file.c b/esp32/littlefs/vfs_littlefs_file.c index 5d0d31e415..7f59350b3c 100644 --- a/esp32/littlefs/vfs_littlefs_file.c +++ b/esp32/littlefs/vfs_littlefs_file.c @@ -91,7 +91,7 @@ STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, xSemaphoreTake(self->littlefs->mutex, portMAX_DELAY); int res = lfs_file_sync(&self->littlefs->lfs, &self->fp); - xSemaphoreGive(self->littlefs->mutex);; + xSemaphoreGive(self->littlefs->mutex); if (res < 0) { *errcode = littleFsErrorToErrno(res); @@ -108,6 +108,9 @@ STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, *errcode = littleFsErrorToErrno(res); return MP_STREAM_ERROR; } + // Free up the object so GC does not need to do that + m_del_obj(pyb_file_obj_t, self); + return 0; } else { *errcode = MP_EINVAL; @@ -170,7 +173,6 @@ STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_ar m_free((void*)fname); if (res < LFS_ERR_OK) { - littlefs_free_up_attributes(&o->cfg); m_del_obj(pyb_file_obj_t, o); mp_raise_OSError(littleFsErrorToErrno(res)); } From c9f5a8a775d86e9e145d5a2b7198ac26377a6525 Mon Sep 17 00:00:00 2001 From: iwahdan88 Date: Wed, 7 Aug 2019 20:24:20 +0200 Subject: [PATCH 05/15] [PYFW-341] Updated the Lorawan with various patches from the semtech repo #close --- .issuetracker | 7 + esp32/mods/modlora.c | 18 +- lib/lora/mac/LoRaMac.c | 486 +++++++++++++++-------- lib/lora/mac/LoRaMac.h | 87 +++- lib/lora/mac/region/Region.h | 11 +- lib/lora/mac/region/RegionAS923.c | 8 +- lib/lora/mac/region/RegionAU915.c | 11 +- lib/lora/mac/region/RegionCN470.c | 8 +- lib/lora/mac/region/RegionEU868.c | 14 +- lib/lora/mac/region/RegionIN865.c | 79 ++-- lib/lora/mac/region/RegionKR920.c | 8 +- lib/lora/mac/region/RegionUS915-Hybrid.c | 16 +- lib/lora/mac/region/RegionUS915.c | 2 +- 13 files changed, 546 insertions(+), 209 deletions(-) create mode 100644 .issuetracker diff --git a/.issuetracker b/.issuetracker new file mode 100644 index 0000000000..21c2846606 --- /dev/null +++ b/.issuetracker @@ -0,0 +1,7 @@ +# Integration with Issue Tracker +# +# (note that '\' need to be escaped). + +[issuetracker "PYFW jira rule"] + regex = "PYFW-(\\d+)" + url = "https://pycomiot.atlassian.net/browse/PYFW-$1" diff --git a/esp32/mods/modlora.c b/esp32/mods/modlora.c index ca018e1d5f..617332e665 100644 --- a/esp32/mods/modlora.c +++ b/esp32/mods/modlora.c @@ -770,6 +770,21 @@ static void OnTxNextActReqTimerEvent(void) { } } +static void MlmeIndication( MlmeIndication_t *mlmeIndication ) +{ + switch( mlmeIndication->MlmeIndication ) + { + case MLME_SCHEDULE_UPLINK: + {// The MAC signals that we shall provide an uplink as soon as possible + printf("Trying to send uplink\n"); + OnTxNextActReqTimerEvent( ); + break; + } + default: + break; + } +} + static void TASK_LoRa (void *pvParameters) { MibRequestConfirm_t mibReq; MlmeReq_t mlmeReq; @@ -796,6 +811,7 @@ static void TASK_LoRa (void *pvParameters) { LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm; LoRaMacPrimitives.MacMcpsIndication = McpsIndication; LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm; + LoRaMacPrimitives.MacMlmeIndication = MlmeIndication; LoRaMacCallbacks.GetBatteryLevel = BoardGetBatteryLevel; LoRaMacInitialization(&LoRaMacPrimitives, &LoRaMacCallbacks, task_cmd_data.info.init.region); @@ -1075,7 +1091,7 @@ static void TASK_LoRa (void *pvParameters) { mlmeReq.Req.Join.AppEui = (uint8_t *)lora_obj.u.otaa.AppEui; mlmeReq.Req.Join.AppKey = (uint8_t *)lora_obj.u.otaa.AppKey; mlmeReq.Req.Join.NbTrials = 1; - mlmeReq.Req.Join.DR = (uint8_t) lora_obj.otaa_dr; + mlmeReq.Req.Join.Datarate = (uint8_t) lora_obj.otaa_dr; LoRaMacMlmeRequest( &mlmeReq ); } else { mibReq.Type = MIB_NETWORK_ACTIVATION; diff --git a/lib/lora/mac/LoRaMac.c b/lib/lora/mac/LoRaMac.c index 64f8bba189..789d74a98b 100644 --- a/lib/lora/mac/LoRaMac.c +++ b/lib/lora/mac/LoRaMac.c @@ -257,7 +257,7 @@ static uint8_t MaxDCycle = 0; static uint16_t AggregatedDCycle; static TimerTime_t AggregatedLastTxDoneTime; static TimerTime_t AggregatedTimeOff; -static TimerEvent_t RadioTxDoneClassCTimer; +//static TimerEvent_t RadioTxDoneClassCTimer; /*! * Enables/Disables duty cycle management (Test only) @@ -396,6 +396,11 @@ static McpsIndication_t McpsIndication; */ static McpsConfirm_t McpsConfirm; +/*! + * Structure to hold MLME indication data. + */ +static MlmeIndication_t MlmeIndication; + /*! * Structure to hold MLME confirm data. */ @@ -462,6 +467,19 @@ static void OnRxWindow1TimerEvent( void ); */ static void OnRxWindow2TimerEvent( void ); +/*! + * \brief Check if the OnAckTimeoutTimer has do be disabled. If so, the + * function disables it. + * + * \param [IN] nodeAckRequested Set to true, if the node has requested an ACK + * \param [IN] class The device class + * \param [IN] ackReceived Set to true, if the node has received an ACK + * \param [IN] ackTimeoutRetriesCounter Retries counter for confirmed uplinks + * \param [IN] ackTimeoutRetries Maximum retries for confirmed uplinks + */ +static void CheckToDisableAckTimeout( bool nodeAckRequested, DeviceClass_t devClass, bool ackReceived, + uint8_t ackTimeoutRetriesCounter, uint8_t ackTimeoutRetries ); + /*! * \brief Function executed on AckTimeout timer event */ @@ -475,6 +493,19 @@ static void OnAckTimeoutTimerEvent( void ); */ static void RxWindowSetup( bool rxContinuous, uint32_t maxRxWindow ); +/*! + * \brief Verifies if sticky MAC commands are pending. + * + * \retval [true: sticky MAC commands pending, false: No MAC commands pending] + */ +static bool IsStickyMacCommandPending( void ); + +/*! + * \brief Configures the events to trigger an MLME-Indication with + * a MLME type of MLME_SCHEDULE_UPLINK. + */ +static void SetMlmeScheduleUplinkIndication( void ); + /*! * \brief Adds a new MAC command to be sent. * @@ -508,6 +539,24 @@ static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ); */ static uint8_t ParseMacCommandsToRepeat( uint8_t* cmdBufIn, uint8_t length, uint8_t* cmdBufOut ); +/*! + * \brief Validates if the payload fits into the frame, taking the datarate + * into account. + * + * \details Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0 + * + * \param lenN Length of the application payload. The length depends on the + * datarate and is region specific + * + * \param datarate Current datarate + * + * \param fOptsLen Length of the fOpts field + * + * \retval [false: payload does not fit into the frame, true: payload fits into + * the frame] + */ +bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ); + /*! * \brief Decodes MAC commands in the fOpts field and in the payload */ @@ -540,9 +589,11 @@ LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl /* * \brief Schedules the frame according to the duty cycle * + * \param [IN] allowDelayedTx When set to true, the a frame will be delayed, + * the duty cycle restriction is active * \retval Status of the operation */ -static LoRaMacStatus_t ScheduleTx( void ); +static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ); /* * \brief Calculates the back-off time for the band of a channel. @@ -589,6 +640,21 @@ LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint */ static void ResetMacParameters( void ); +/*! + * \brief Resets MAC specific parameters to default + * + * \param [IN] fPort The fPort + * + * \retval [false: fPort not allowed, true: fPort allowed] + */ +static bool IsFPortAllowed( uint8_t fPort ); + +/*! + * \brief Opens up a continuous RX 2 window. This is used for + * class c devices. + */ +static void OpenContinuousRx2Window( void ); + static IRAM_ATTR void OnRadioTxDone( void ) { GetPhyParams_t getPhy; @@ -602,7 +668,7 @@ static IRAM_ATTR void OnRadioTxDone( void ) } else { - TimerStart(&RadioTxDoneClassCTimer); + OpenContinuousRx2Window( ); } // Setup timers @@ -686,7 +752,6 @@ static void OnRadioRxDone( uint8_t *payload, uint32_t timestamp, uint16_t size, ApplyCFListParams_t applyCFList; GetPhyParams_t getPhy; PhyParam_t phyParam; - bool skipIndication = false; uint8_t pktHeaderLen = 0; uint32_t address = 0; @@ -937,8 +1002,8 @@ static void OnRadioRxDone( uint8_t *payload, uint32_t timestamp, uint16_t size, // In this case, the MAC layer shall accept the MAC commands // which are included in the downlink retransmission. // It should not provide the same frame to the application - // layer again. - skipIndication = true; + // layer again. The MAC layer accepts the acknowledgement. + LoRaMacFlags.Bits.McpsIndSkip = 1; } } else @@ -967,6 +1032,9 @@ static void OnRadioRxDone( uint8_t *payload, uint32_t timestamp, uint16_t size, if( fCtrl.Bits.Ack == 1 ) {// Reset MacCommandsBufferIndex when we have received an ACK. MacCommandsBufferIndex = 0; + // Update acknowledgement information + McpsConfirm.AckReceived = fCtrl.Bits.Ack; + McpsIndication.AckReceived = fCtrl.Bits.Ack; } } else @@ -1000,7 +1068,10 @@ static void OnRadioRxDone( uint8_t *payload, uint32_t timestamp, uint16_t size, } else { - skipIndication = true; + LoRaMacFlags.Bits.McpsIndSkip = 1; + // This is not a valid frame. Drop it and reset the ACK bits + McpsConfirm.AckReceived = false; + McpsIndication.AckReceived = false; } } else @@ -1019,12 +1090,9 @@ static void OnRadioRxDone( uint8_t *payload, uint32_t timestamp, uint16_t size, downLinkCounter, LoRaMacRxPayload ); - if( skipIndication == false ) - { - McpsIndication.Buffer = LoRaMacRxPayload; - McpsIndication.BufferSize = frameLen; - McpsIndication.RxData = true; - } + McpsIndication.Buffer = LoRaMacRxPayload; + McpsIndication.BufferSize = frameLen; + McpsIndication.RxData = true; } } else @@ -1036,34 +1104,9 @@ static void OnRadioRxDone( uint8_t *payload, uint32_t timestamp, uint16_t size, } } - if( skipIndication == false ) - { - // Check if the frame is an acknowledgement - if( fCtrl.Bits.Ack == 1 ) - { - McpsConfirm.AckReceived = true; - McpsIndication.AckReceived = true; - - // Stop the AckTimeout timer as no more retransmissions - // are needed. - TimerStop( &AckTimeoutTimer ); - } - else - { - McpsConfirm.AckReceived = false; - - if( AckTimeoutRetriesCounter > AckTimeoutRetries ) - { - // Stop the AckTimeout timer as no more retransmissions - // are needed. - TimerStop( &AckTimeoutTimer ); - } - } - } // Provide always an indication, skip the callback to the user application, // in case of a confirmed downlink retransmission. LoRaMacFlags.Bits.McpsInd = 1; - LoRaMacFlags.Bits.McpsIndSkip = skipIndication; } else { @@ -1091,11 +1134,19 @@ static void OnRadioRxDone( uint8_t *payload, uint32_t timestamp, uint16_t size, PrepareRxDoneAbort( ); break; } - LoRaMacFlags.Bits.MacDone = 1; - // Trig OnMacCheckTimerEvent call as soon as possible - TimerSetValue( &MacStateCheckTimer, 1 ); - TimerStart( &MacStateCheckTimer ); + // Verify if we need to disable the AckTimeoutTimer + CheckToDisableAckTimeout( NodeAckRequested, LoRaMacDeviceClass, McpsConfirm.AckReceived, + AckTimeoutRetriesCounter, AckTimeoutRetries ); + + if( AckTimeoutTimer.IsRunning == false ) + {// Procedure is completed when the AckTimeoutTimer is not running anymore + LoRaMacFlags.Bits.MacDone = 1; + + // Trig OnMacCheckTimerEvent call as soon as possible + TimerSetValue( &MacStateCheckTimer, 1 ); + TimerStart( &MacStateCheckTimer ); + } } static void OnRadioTxTimeout( void ) @@ -1106,7 +1157,7 @@ static void OnRadioTxTimeout( void ) } else { - OnRxWindow2TimerEvent( ); + OpenContinuousRx2Window( ); } McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; @@ -1120,10 +1171,6 @@ static void OnRadioRxError( void ) { Radio.Sleep( ); } - else - { - OnRxWindow2TimerEvent( ); - } if( RxSlot == 0 ) { @@ -1133,9 +1180,13 @@ static void OnRadioRxError( void ) } MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR; - if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay + 50) + if( LoRaMacDeviceClass != CLASS_C ) { - LoRaMacFlags.Bits.MacDone = 1; + if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay ) + { + TimerStop( &RxWindowTimer2 ); + LoRaMacFlags.Bits.MacDone = 1; + } } } else @@ -1145,7 +1196,16 @@ static void OnRadioRxError( void ) McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; } MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; - LoRaMacFlags.Bits.MacDone = 1; + + if( LoRaMacDeviceClass != CLASS_C ) + { + LoRaMacFlags.Bits.MacDone = 1; + } + } + + if( LoRaMacDeviceClass == CLASS_C ) + { + OpenContinuousRx2Window( ); } } @@ -1155,11 +1215,7 @@ static void OnRadioRxTimeout( void ) { Radio.Sleep( ); } - else - { - OnRxWindow2TimerEvent( ); - } - + if( RxSlot == 0 ) { if( NodeAckRequested == true ) @@ -1168,9 +1224,13 @@ static void OnRadioRxTimeout( void ) } MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT; - if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay + 50) + if( LoRaMacDeviceClass != CLASS_C ) { - LoRaMacFlags.Bits.MacDone = 1; + if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay ) + { + TimerStop( &RxWindowTimer2 ); + LoRaMacFlags.Bits.MacDone = 1; + } } } else @@ -1186,6 +1246,11 @@ static void OnRadioRxTimeout( void ) LoRaMacFlags.Bits.MacDone = 1; } } + + if( LoRaMacDeviceClass == CLASS_C ) + { + OpenContinuousRx2Window( ); + } } static void OnMacStateCheckTimerEvent( void ) @@ -1231,23 +1296,9 @@ static void OnMacStateCheckTimerEvent( void ) {// Node joined successfully UpLinkCounter = 0; ChannelsNbRepCounter = 0; - LoRaMacState &= ~LORAMAC_TX_RUNNING; - } - else - { - // Pycom: Do not resend join requests - - // if( JoinRequestTrials >= MaxJoinRequestTrials ) - // { - LoRaMacState &= ~LORAMAC_TX_RUNNING; - // } - // else - // { - // LoRaMacFlags.Bits.MacDone = 0; - // // Sends the same frame again - // OnTxDelayedTimerEvent( ); - // } + //LoRaMacState &= ~LORAMAC_TX_RUNNING; } + LoRaMacState &= ~LORAMAC_TX_RUNNING; } else {// Procedure for all other frames @@ -1310,8 +1361,8 @@ static void OnMacStateCheckTimerEvent( void ) phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); LoRaMacParams.ChannelsDatarate = phyParam.Value; } - // Try to send the frame again - if( ScheduleTx( ) == LORAMAC_STATUS_OK ) + // Try to send the frame again. Allow delayed frame transmissions + if( ScheduleTx( true ) == LORAMAC_STATUS_OK ) { LoRaMacFlags.Bits.MacDone = 0; } @@ -1355,32 +1406,24 @@ static void OnMacStateCheckTimerEvent( void ) LoRaMacState &= ~LORAMAC_RX; } - if( LoRaMacFlags.Bits.McpsInd == 1 ) - { - if( LoRaMacDeviceClass == CLASS_C ) - {// Activate RX2 window for Class C - OnRxWindow2TimerEvent( ); - } - if( LoRaMacFlags.Bits.McpsIndSkip == 0 ) - { - LoRaMacPrimitives->MacMcpsIndication( &McpsIndication ); - } - LoRaMacFlags.Bits.McpsIndSkip = 0; - LoRaMacFlags.Bits.McpsInd = 0; - } - if( LoRaMacState == LORAMAC_IDLE ) { if( LoRaMacFlags.Bits.McpsReq == 1 ) { - LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm ); LoRaMacFlags.Bits.McpsReq = 0; + LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm ); } if( LoRaMacFlags.Bits.MlmeReq == 1 ) { - LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm ); LoRaMacFlags.Bits.MlmeReq = 0; + LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm ); + } + + // Verify if sticky MAC commands are pending or not + if( IsStickyMacCommandPending( ) == true ) + {// Setup MLME indication + SetMlmeScheduleUplinkIndication( ); } // Procedure done. Reset variables. @@ -1392,37 +1435,37 @@ static void OnMacStateCheckTimerEvent( void ) TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); TimerStart( &MacStateCheckTimer ); } + + // Handle MCPS indication + if( LoRaMacFlags.Bits.McpsInd == 1 ) + { + LoRaMacFlags.Bits.McpsInd = 0; + if( LoRaMacDeviceClass == CLASS_C ) + {// Activate RX2 window for Class C + OpenContinuousRx2Window( ); + } + if( LoRaMacFlags.Bits.McpsIndSkip == 0 ) + { + LoRaMacPrimitives->MacMcpsIndication( &McpsIndication ); + } + LoRaMacFlags.Bits.McpsIndSkip = 0; + } + + // Handle MLME indication + if( LoRaMacFlags.Bits.MlmeInd == 1 ) + { + LoRaMacFlags.Bits.MlmeInd = 0; + LoRaMacPrimitives->MacMlmeIndication( &MlmeIndication ); + } } static void OnTxDelayedTimerEvent( void ) { - LoRaMacHeader_t macHdr; - LoRaMacFrameCtrl_t fCtrl; - AlternateDrParams_t altDr; - TimerStop( &TxDelayedTimer ); LoRaMacState &= ~LORAMAC_TX_DELAYED; - if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) ) - { - ResetMacParameters( ); - - altDr.NbTrials = JoinRequestTrials + 1; - LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, &altDr ); - - macHdr.Value = 0; - macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; - - fCtrl.Value = 0; - fCtrl.Bits.Adr = AdrCtrlOn; - - /* In case of join request retransmissions, the stack must prepare - * the frame again, because the network server keeps track of the random - * LoRaMacDevNonce values to prevent reply attacks. */ - PrepareFrame( &macHdr, &fCtrl, 0, NULL, 0 ); - } - - ScheduleTx( ); + // Schedule frame, allow delayed frame transmissions + ScheduleTx( true ); } static void OnRxWindow1TimerEvent( void ) @@ -1435,7 +1478,7 @@ static void OnRxWindow1TimerEvent( void ) RxWindow1Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; RxWindow1Config.RepeaterSupport = RepeaterSupport; RxWindow1Config.RxContinuous = false; - RxWindow1Config.Window = RxSlot; + RxWindow1Config.RxSlot = RxSlot; if( LoRaMacDeviceClass == CLASS_C ) { @@ -1454,7 +1497,7 @@ static void OnRxWindow2TimerEvent( void ) RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency; RxWindow2Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; RxWindow2Config.RepeaterSupport = RepeaterSupport; - RxWindow2Config.Window = 1; + RxWindow2Config.RxSlot = 1; if( LoRaMacDeviceClass != CLASS_C ) { @@ -1462,16 +1505,52 @@ static void OnRxWindow2TimerEvent( void ) } else { + // Setup continuous listening for class c RxWindow2Config.RxContinuous = true; - if (NetworkActivation == ACTIVATION_TYPE_ABP){ + /*if (NetworkActivation == ACTIVATION_TYPE_ABP){ RxWindow2Config.Datarate = McpsIndication.RxDatarate; - } + }*/ } if( RegionRxConfig( LoRaMacRegion, &RxWindow2Config, ( int8_t* )&McpsIndication.RxDatarate ) == true ) { RxWindowSetup( RxWindow2Config.RxContinuous, LoRaMacParams.MaxRxWindow ); - RxSlot = RxWindow2Config.Window; + RxSlot = 1; + } +} + +static void CheckToDisableAckTimeout( bool nodeAckRequested, DeviceClass_t devClass, bool ackReceived, + uint8_t ackTimeoutRetriesCounter, uint8_t ackTimeoutRetries ) +{ + // There are three cases where we need to stop the AckTimeoutTimer: + if( nodeAckRequested == false ) + { + if( devClass == CLASS_C ) + {// FIRST CASE + // We have performed an unconfirmed uplink in class c mode + // and have received a downlink in RX1 or RX2. + TimerStop( &AckTimeoutTimer ); + } + } + else + { + if( ackReceived == 1 ) + {// SECOND CASE + // We have performed a confirmed uplink and have received a + // downlink with a valid ACK. + TimerStop( &AckTimeoutTimer ); + } + else + {// THIRD CASE + if( ackTimeoutRetriesCounter > ackTimeoutRetries ) + { + // We have performed a confirmed uplink and have not + // received a downlink with a valid ACK. In this case + // we need to verify if the maximum retries have been + // elapsed. If so, stop the timer. + TimerStop( &AckTimeoutTimer ); + } + } } } @@ -1533,6 +1612,22 @@ bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ) return false; } +static bool IsStickyMacCommandPending( void ) +{ + if( MacCommandsBufferToRepeatIndex > 0 ) + { + // Sticky MAC commands pending + return true; + } + return false; +} + +static void SetMlmeScheduleUplinkIndication( void ) +{ + MlmeIndication.MlmeIndication = MLME_SCHEDULE_UPLINK; + LoRaMacFlags.Bits.MlmeInd = 1; +} + static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ) { LoRaMacStatus_t status = LORAMAC_STATUS_BUSY; @@ -1572,6 +1667,8 @@ static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ) MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; // Status: Datarate ACK, Channel ACK MacCommandsBuffer[MacCommandsBufferIndex++] = p1; + // This is a sticky MAC command answer. Setup indication + SetMlmeScheduleUplinkIndication( ); status = LORAMAC_STATUS_OK; } break; @@ -1600,6 +1697,8 @@ static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ) { MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; // No payload for this answer + // This is a sticky MAC command answer. Setup indication + SetMlmeScheduleUplinkIndication( ); status = LORAMAC_STATUS_OK; } break; @@ -1617,6 +1716,10 @@ static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ) MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; // Status: Uplink frequency exists, Channel frequency OK MacCommandsBuffer[MacCommandsBufferIndex++] = p1; + + // This is a sticky MAC command answer. Setup indication + SetMlmeScheduleUplinkIndication( ); + status = LORAMAC_STATUS_OK; } break; @@ -1772,7 +1875,7 @@ static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t comm { batteryLevel = LoRaMacCallbacks->GetBatteryLevel( ); } - AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, batteryLevel, snr ); + AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, batteryLevel, snr & 0x3F); break; } case SRV_MAC_NEW_CHANNEL_REQ: @@ -1888,13 +1991,15 @@ LoRaMacStatus_t Send( LoRaMacHeader_t *macHdr, uint8_t fPort, void *fBuffer, uin McpsConfirm.AckReceived = false; McpsConfirm.UpLinkCounter = UpLinkCounter; - status = ScheduleTx( ); + // Schedule frame, do not allow delayed transmissions + status = ScheduleTx( false ); return status; } -static LoRaMacStatus_t ScheduleTx( void ) +static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ) { + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; TimerTime_t dutyCycleTimeOff = 0; NextChanParams_t nextChan; @@ -1918,12 +2023,27 @@ static LoRaMacStatus_t ScheduleTx( void ) nextChan.LastAggrTx = AggregatedLastTxDoneTime; // Select channel - while( RegionNextChannel( LoRaMacRegion, &nextChan, &Channel, &dutyCycleTimeOff, &AggregatedTimeOff ) == false ) + status = RegionNextChannel( LoRaMacRegion, &nextChan, &Channel, &dutyCycleTimeOff, &AggregatedTimeOff ); + + if( status != LORAMAC_STATUS_OK ) { - // Set the default datarate - LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate; - // Update datarate in the function parameters - nextChan.Datarate = LoRaMacParams.ChannelsDatarate; + if( ( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) && + ( allowDelayedTx == true ) ) + { + // Allow delayed transmissions. We have to allow it in case + // the MAC must retransmit a frame with the frame repetitions + if( dutyCycleTimeOff != 0 ) + {// Send later - prepare timer + LoRaMacState |= LORAMAC_TX_DELAYED; + TimerSetValue( &TxDelayedTimer, dutyCycleTimeOff ); + TimerStart( &TxDelayedTimer ); + } + return LORAMAC_STATUS_OK; + } + else + {// State where the MAC cannot send a frame + return status; + } } // Compute Rx1 windows parameters @@ -1941,8 +2061,8 @@ static LoRaMacStatus_t ScheduleTx( void ) if( IsLoRaMacNetworkJoined == false ) { - RxWindow1Delay = LoRaMacParams.JoinAcceptDelay1 + RxWindow1Config.WindowOffset - 2; - RxWindow2Delay = LoRaMacParams.JoinAcceptDelay2 + RxWindow2Config.WindowOffset - 3; + RxWindow1Delay = LoRaMacParams.JoinAcceptDelay1 + RxWindow1Config.WindowOffset; + RxWindow2Delay = LoRaMacParams.JoinAcceptDelay2 + RxWindow2Config.WindowOffset; } else { @@ -1950,11 +2070,14 @@ static LoRaMacStatus_t ScheduleTx( void ) { return LORAMAC_STATUS_LENGTH_ERROR; } - RxWindow1Delay = LoRaMacParams.ReceiveDelay1 + RxWindow1Config.WindowOffset - 1; - RxWindow2Delay = LoRaMacParams.ReceiveDelay2 + RxWindow2Config.WindowOffset - 2; + RxWindow1Delay = LoRaMacParams.ReceiveDelay1 + RxWindow1Config.WindowOffset; + RxWindow2Delay = LoRaMacParams.ReceiveDelay2 + RxWindow2Config.WindowOffset; } - // Schedule transmission of frame + // Try to send now + return SendFrameOnChannel( Channel ); + + /* // Schedule transmission of frame if( dutyCycleTimeOff == 0 ) { // Try to send now @@ -1968,7 +2091,7 @@ static LoRaMacStatus_t ScheduleTx( void ) TimerStart( &TxDelayedTimer ); return LORAMAC_STATUS_OK; - } + } */ } static void CalculateBackOff( uint8_t channel ) @@ -1985,8 +2108,9 @@ static void CalculateBackOff( uint8_t channel ) // Update regional back-off RegionCalcBackOff( LoRaMacRegion, &calcBackOff ); - // Update aggregated time-off - AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir ); + // Update aggregated time-off. This must be an assignment and no incremental + // update as we do only calculate the time-off based on the last transmission + AggregatedTimeOff = ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir ); } static void ResetMacParameters( void ) @@ -2021,6 +2145,9 @@ static void ResetMacParameters( void ) LoRaMacParams.MaxEirp = LoRaMacParamsDefaults.MaxEirp; LoRaMacParams.AntennaGain = LoRaMacParamsDefaults.AntennaGain; + // Reset to application defaults + RegionInitDefaults( LoRaMacRegion, INIT_TYPE_APP_DEFAULTS ); + NodeAckRequested = false; SrvAckRequested = false; MacCommandsInNextTx = false; @@ -2038,6 +2165,21 @@ static void ResetMacParameters( void ) LastTxChannel = Channel; } +static bool IsFPortAllowed( uint8_t fPort ) +{ + if( fPort > 224 ) + { + return false; + } + return true; +} + +static void OpenContinuousRx2Window( void ) +{ + OnRxWindow2TimerEvent( ); + RxSlot = 2; +} + LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) { AdrNextParams_t adrNext; @@ -2222,6 +2364,7 @@ LoRaMacStatus_t SendFrameOnChannel( uint8_t channel ) McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; McpsConfirm.Datarate = LoRaMacParams.ChannelsDatarate; McpsConfirm.TxPower = txPower; + McpsConfirm.Channel = channel; McpsConfirm.UpLinkFrequency = Radio.GetChannel(); // Store the time on air @@ -2292,7 +2435,8 @@ LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacC if( ( primitives->MacMcpsConfirm == NULL ) || ( primitives->MacMcpsIndication == NULL ) || - ( primitives->MacMlmeConfirm == NULL ) ) + ( primitives->MacMlmeConfirm == NULL ) || + ( primitives->MacMlmeIndication == NULL ) ) { return LORAMAC_STATUS_PARAMETER_INVALID; } @@ -2407,8 +2551,8 @@ LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacC TimerInit( &RxWindowTimer1, OnRxWindow1TimerEvent ); TimerInit( &RxWindowTimer2, OnRxWindow2TimerEvent ); TimerInit( &AckTimeoutTimer, OnAckTimeoutTimerEvent ); - TimerInit( &RadioTxDoneClassCTimer, OnRxWindow2TimerEvent ); - TimerSetValue( &RadioTxDoneClassCTimer, 1 ); + //TimerInit( &RadioTxDoneClassCTimer, OnRxWindow2TimerEvent ); + //TimerSetValue( &RadioTxDoneClassCTimer, 1 ); // Store the current initialization time LoRaMacInitializationTime = TimerGetCurrentTime( ); @@ -2670,6 +2814,11 @@ LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet ) mibGet->Param.AntennaGain = LoRaMacParams.AntennaGain; break; } + case MIB_DEFAULT_ANTENNA_GAIN: + { + mibGet->Param.DefaultAntennaGain = LoRaMacParamsDefaults.AntennaGain; + break; + } default: status = LORAMAC_STATUS_SERVICE_UNKNOWN; break; @@ -2714,7 +2863,15 @@ LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ) { // Set the NodeAckRequested indicator to default NodeAckRequested = false; - OnRxWindow2TimerEvent( ); + // Set the radio into sleep mode in case we are still in RX mode + Radio.Sleep( ); + // Compute Rx2 windows parameters in case the RX2 datarate has changed + RegionComputeRxWindowParameters( LoRaMacRegion, + LoRaMacParams.Rx2Channel.Datarate, + LoRaMacParams.MinRxSymbols, + LoRaMacParams.SystemMaxRxError, + &RxWindow2Config ); + OpenContinuousRx2Window( ); break; } } @@ -2800,29 +2957,18 @@ LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ) if( ( LoRaMacDeviceClass == CLASS_C ) && ( IsLoRaMacNetworkJoined == true ) ) { + // We can only compute the RX window parameters directly, if we are already + // in class c mode and joined. We cannot setup an RX window in case of any other + // class type. + // Set the radio into sleep mode in case we are still in RX mode + Radio.Sleep( ); // Compute Rx2 windows parameters RegionComputeRxWindowParameters( LoRaMacRegion, LoRaMacParams.Rx2Channel.Datarate, LoRaMacParams.MinRxSymbols, LoRaMacParams.SystemMaxRxError, &RxWindow2Config ); - - RxWindow2Config.Channel = Channel; - RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency; - RxWindow2Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; - RxWindow2Config.RepeaterSupport = RepeaterSupport; - RxWindow2Config.Window = 1; - RxWindow2Config.RxContinuous = true; - - if( RegionRxConfig( LoRaMacRegion, &RxWindow2Config, ( int8_t* )&McpsIndication.RxDatarate ) == true ) - { - RxWindowSetup( RxWindow2Config.RxContinuous, LoRaMacParams.MaxRxWindow ); - RxSlot = RxWindow2Config.Window; - } - else - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } + OpenContinuousRx2Window( ); } } else @@ -2923,6 +3069,7 @@ LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ) case MIB_CHANNELS_DATARATE: { verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate; + verify.DatarateParams.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; if( RegionVerify( LoRaMacRegion, &verify, PHY_TX_DR ) == true ) { @@ -2987,6 +3134,11 @@ LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ) LoRaMacParams.AntennaGain = mibSet->Param.AntennaGain; break; } + case MIB_DEFAULT_ANTENNA_GAIN: + { + LoRaMacParamsDefaults.AntennaGain = mibSet->Param.DefaultAntennaGain; + break; + } default: status = LORAMAC_STATUS_SERVICE_UNKNOWN; break; @@ -3177,7 +3329,7 @@ LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ) { return LORAMAC_STATUS_PARAMETER_INVALID; } - if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + if( LoRaMacState != LORAMAC_IDLE ) { return LORAMAC_STATUS_BUSY; } @@ -3203,6 +3355,8 @@ LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ) return LORAMAC_STATUS_PARAMETER_INVALID; } + ResetMacParameters( ); + // Verify the parameter NbTrials for the join procedure verify.NbJoinTrials = mlmeRequest->Req.Join.NbTrials; @@ -3220,6 +3374,7 @@ LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ) LoRaMacDevEui = mlmeRequest->Req.Join.DevEui; LoRaMacAppEui = mlmeRequest->Req.Join.AppEui; LoRaMacAppKey = mlmeRequest->Req.Join.AppKey; + //LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, mlmeRequest->Req.Join.Datarate ); MaxJoinRequestTrials = mlmeRequest->Req.Join.NbTrials; // Reset variable JoinRequestTrials @@ -3229,12 +3384,12 @@ LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ) macHdr.Value = 0; macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; - ResetMacParameters( ); + //ResetMacParameters( ); altDr.NbTrials = JoinRequestTrials + 1; // Pycom: Add Join DR selection as and when the region permits - RegionForceJoinDataRate( LoRaMacRegion, mlmeRequest->Req.Join.DR, &altDr ); + RegionForceJoinDataRate( LoRaMacRegion, mlmeRequest->Req.Join.Datarate, &altDr ); LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, &altDr ); @@ -3287,15 +3442,14 @@ LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest ) uint8_t fPort = 0; void *fBuffer; uint16_t fBufferSize; - int8_t datarate; + int8_t datarate = DR_0; bool readyToSend = false; if( mcpsRequest == NULL ) { return LORAMAC_STATUS_PARAMETER_INVALID; } - if( ( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) || - ( ( LoRaMacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED ) ) + if( LoRaMacState != LORAMAC_IDLE ) { return LORAMAC_STATUS_BUSY; } @@ -3348,6 +3502,12 @@ LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest ) break; } + // Filter fPorts + if( IsFPortAllowed( fPort ) == false ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + // Get the minimum possible datarate getPhy.Attribute = PHY_MIN_TX_DR; getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; diff --git a/lib/lora/mac/LoRaMac.h b/lib/lora/mac/LoRaMac.h index 8eb05415fa..5c9af97c1b 100644 --- a/lib/lora/mac/LoRaMac.h +++ b/lib/lora/mac/LoRaMac.h @@ -263,6 +263,30 @@ typedef struct sRx2ChannelParams uint8_t Datarate; }Rx2ChannelParams_t; +/*! + * LoRaMAC receive window enumeration + */ +typedef enum eLoRaMacRxSlot +{ + /*! + * LoRaMAC receive window 1 + */ + RX_SLOT_WIN_1, + /*! + * LoRaMAC receive window 2 + */ + RX_SLOT_WIN_2, + /*! + * LoRaMAC receive window 2 for class c - continuous listening + */ + RX_SLOT_WIN_CLASS_C, + /*! + * LoRaMAC class b ping slot window + */ + RX_SLOT_WIN_PING_SLOT +}LoRaMacRxSlot_t; + + /*! * Global MAC layer parameters */ @@ -680,6 +704,10 @@ typedef union eLoRaMacFlags_t * MLME-Req pending */ uint8_t MlmeReq : 1; + /*! + * MLME-Ind pending + */ + uint8_t MlmeInd : 1; /*! * MAC cycle done */ @@ -893,6 +921,10 @@ typedef struct sMcpsConfirm * The uplink frequency related to the frame */ uint32_t UpLinkFrequency; + /*! + * The uplink channel related to the frame + */ + uint32_t Channel; }McpsConfirm_t; /*! @@ -1010,6 +1042,11 @@ typedef enum eMlme * LoRaWAN end-device certification */ MLME_TXCW_1, + /*! + * Indicates that the application shall perform an uplink as + * soon as possible. + */ + MLME_SCHEDULE_UPLINK }Mlme_t; /*! @@ -1040,9 +1077,9 @@ typedef struct sMlmeReqJoin */ uint8_t NbTrials; /*! - * To support initial data rate selection for the Join Request + * Datarate used for join request. */ - uint8_t DR; + uint8_t Datarate; }MlmeReqJoin_t; /*! @@ -1122,6 +1159,17 @@ typedef struct sMlmeConfirm uint8_t NbRetries; }MlmeConfirm_t; +/*! + * LoRaMAC MLME-Indication primitive + */ +typedef struct sMlmeIndication +{ + /*! + * MLME-Indication type + */ + Mlme_t MlmeIndication; +}MlmeIndication_t; + /*! * LoRa Mac Information Base (MIB) * @@ -1371,7 +1419,14 @@ typedef enum eMib * The formula is: * radioTxPower = ( int8_t )floor( maxEirp - antennaGain ) */ - MIB_ANTENNA_GAIN + MIB_ANTENNA_GAIN, + /*! + * Default antenna gain of the node. Default value is region specific. + * The antenna gain is used to calculate the TX power of the node. + * The formula is: + * radioTxPower = ( int8_t )floor( maxEirp - antennaGain ) + */ + MIB_DEFAULT_ANTENNA_GAIN }Mib_t; /*! @@ -1565,6 +1620,12 @@ typedef union uMibParam * Related MIB type: \ref MIB_ANTENNA_GAIN */ float AntennaGain; + /*! + * Default antenna gain + * + * Related MIB type: \ref MIB_DEFAULT_ANTENNA_GAIN + */ + float DefaultAntennaGain; }MibParam_t; /*! @@ -1647,7 +1708,19 @@ typedef enum eLoRaMacStatus * Service not started - the specified region is not supported * or not activated with preprocessor definitions. */ - LORAMAC_STATUS_REGION_NOT_SUPPORTED + LORAMAC_STATUS_REGION_NOT_SUPPORTED, + /*! + * + */ + LORAMAC_STATUS_DUTYCYCLE_RESTRICTED, + /*! + * + */ + LORAMAC_STATUS_NO_CHANNEL_FOUND, + /*! + * + */ + LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND }LoRaMacStatus_t; /*! @@ -1721,6 +1794,12 @@ typedef struct sLoRaMacPrimitives * \param [OUT] MLME-Confirm parameters */ void ( *MacMlmeConfirm )( MlmeConfirm_t *MlmeConfirm ); + /*! + * \brief MLME-Indication primitive + * + * \param [OUT] MLME-Indication parameters + */ + void ( *MacMlmeIndication )( MlmeIndication_t *MlmeIndication ); }LoRaMacPrimitives_t; /*! diff --git a/lib/lora/mac/region/Region.h b/lib/lora/mac/region/Region.h index 641f6af810..f426391f24 100644 --- a/lib/lora/mac/region/Region.h +++ b/lib/lora/mac/region/Region.h @@ -672,7 +672,12 @@ typedef enum eInitType /*! * Restores default channels only. */ - INIT_TYPE_RESTORE + INIT_TYPE_RESTORE, + /*! + * Initializes the region specific data to the defaults which were set by + * the application. + */ + INIT_TYPE_APP_DEFAULTS }InitType_t; typedef enum eChannelsMask @@ -902,9 +907,9 @@ typedef struct sRxConfigParams */ bool RxContinuous; /*! - * Sets the RX window. 0: RX window 1, 1: RX window 2. + * Sets the RX window. */ - bool Window; + LoRaMacRxSlot_t RxSlot; }RxConfigParams_t; /*! diff --git a/lib/lora/mac/region/RegionAS923.c b/lib/lora/mac/region/RegionAS923.c index bfc36038d9..3f977820fb 100644 --- a/lib/lora/mac/region/RegionAS923.c +++ b/lib/lora/mac/region/RegionAS923.c @@ -368,6 +368,12 @@ void RegionAS923InitDefaults( InitType_t type ) ChannelsMask[0] |= ChannelsDefaultMask[0]; break; } + case INIT_TYPE_APP_DEFAULTS: + { + // Update the channels mask defaults + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); + break; + } default: { break; @@ -601,7 +607,7 @@ bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->Window == 0 ) + if( rxConfig->RxSlot == 0 ) { // Apply window 1 frequency frequency = Channels[rxConfig->Channel].Frequency; diff --git a/lib/lora/mac/region/RegionAU915.c b/lib/lora/mac/region/RegionAU915.c index e1a63135cb..fe0eb261b4 100644 --- a/lib/lora/mac/region/RegionAU915.c +++ b/lib/lora/mac/region/RegionAU915.c @@ -338,6 +338,15 @@ void RegionAU915InitDefaults( InitType_t type ) } break; } + case INIT_TYPE_APP_DEFAULTS: + { + // Copy channels default mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 ); + break; + } default: { break; @@ -514,7 +523,7 @@ bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->Window == 0 ) + if( rxConfig->RxSlot == 0 ) { // Apply window 1 frequency frequency = AU915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * AU915_STEPWIDTH_RX1_CHANNEL; diff --git a/lib/lora/mac/region/RegionCN470.c b/lib/lora/mac/region/RegionCN470.c index 929646c504..a3bbd10149 100644 --- a/lib/lora/mac/region/RegionCN470.c +++ b/lib/lora/mac/region/RegionCN470.c @@ -318,6 +318,12 @@ void RegionCN470InitDefaults( InitType_t type ) RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); break; } + case INIT_TYPE_APP_DEFAULTS: + { + // Update the channels mask defaults + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); + break; + } default: { break; @@ -477,7 +483,7 @@ bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->Window == 0 ) + if( rxConfig->RxSlot == 0 ) { // Apply window 1 frequency frequency = CN470_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 48 ) * CN470_STEPWIDTH_RX1_CHANNEL; diff --git a/lib/lora/mac/region/RegionEU868.c b/lib/lora/mac/region/RegionEU868.c index da31e1805f..7f0c69b049 100644 --- a/lib/lora/mac/region/RegionEU868.c +++ b/lib/lora/mac/region/RegionEU868.c @@ -359,6 +359,12 @@ void RegionEU868InitDefaults( InitType_t type ) ChannelsMask[0] |= ChannelsDefaultMask[0]; break; } + case INIT_TYPE_APP_DEFAULTS: + { + // Update the channels mask defaults + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); + break; + } default: { break; @@ -573,7 +579,7 @@ bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->Window == 0 ) + if( rxConfig->RxSlot == 0 ) { // Apply window 1 frequency frequency = Channels[rxConfig->Channel].Frequency; @@ -947,7 +953,7 @@ bool RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; *time = 0; - return true; + return LORAMAC_STATUS_OK; } else { @@ -955,12 +961,12 @@ bool RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, { // Delay transmission due to AggregatedTimeOff or to a band time off *time = nextTxDelay; - return true; + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; } // Datarate not supported by any channel, restore defaults ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); *time = 0; - return false; + return LORAMAC_STATUS_NO_CHANNEL_FOUND; } } diff --git a/lib/lora/mac/region/RegionIN865.c b/lib/lora/mac/region/RegionIN865.c index bf85248ac5..07f55e16c4 100644 --- a/lib/lora/mac/region/RegionIN865.c +++ b/lib/lora/mac/region/RegionIN865.c @@ -334,6 +334,12 @@ void RegionIN865InitDefaults( InitType_t type ) ChannelsMask[0] |= ChannelsDefaultMask[0]; break; } + case INIT_TYPE_APP_DEFAULTS: + { + // Update the channels mask defaults + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); + break; + } default: { break; @@ -347,7 +353,14 @@ bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) { case PHY_TX_DR: { - return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ); + if( verify->DatarateParams.Datarate == DR_6 ) + {// DR_6 is not supported by this region + return false; + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ); + } } case PHY_DEF_TX_DR: { @@ -355,7 +368,14 @@ bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) } case PHY_RX_DR: { - return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ); + if( verify->DatarateParams.Datarate == DR_6 ) + {// DR_6 is not supported by this region + return false; + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ); + } } case PHY_DEF_TX_POWER: case PHY_TX_POWER: @@ -547,7 +567,7 @@ bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->Window == 0 ) + if( rxConfig->RxSlot == 0 ) { // Apply window 1 frequency frequency = Channels[rxConfig->Channel].Frequency; @@ -685,29 +705,36 @@ uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, in } } - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; - phyParam = RegionIN865GetPhyParam( &getPhy ); - - linkAdrVerifyParams.Status = status; - linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; - linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; - linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; - linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; - linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; - linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; - linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; - linkAdrVerifyParams.NbChannels = IN865_MAX_NB_CHANNELS; - linkAdrVerifyParams.ChannelsMask = &chMask; - linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; - linkAdrVerifyParams.MaxDatarate = IN865_TX_MAX_DATARATE; - linkAdrVerifyParams.Channels = Channels; - linkAdrVerifyParams.MinTxPower = IN865_MIN_TX_POWER; - linkAdrVerifyParams.MaxTxPower = IN865_MAX_TX_POWER; - - // Verify the parameters and update, if necessary - status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + if( linkAdrParams.Datarate != DR_6 ) + { + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionIN865GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = IN865_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = IN865_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = Channels; + linkAdrVerifyParams.MinTxPower = IN865_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = IN865_MAX_TX_POWER; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + } + else + {// DR_6 is not supported by this region + status &= 0xFD; // Datarate KO + } // Update channelsMask if everything is correct if( status == 0x07 ) diff --git a/lib/lora/mac/region/RegionKR920.c b/lib/lora/mac/region/RegionKR920.c index 8377efee3e..e4785eddba 100644 --- a/lib/lora/mac/region/RegionKR920.c +++ b/lib/lora/mac/region/RegionKR920.c @@ -353,6 +353,12 @@ void RegionKR920InitDefaults( InitType_t type ) ChannelsMask[0] |= ChannelsDefaultMask[0]; break; } + case INIT_TYPE_APP_DEFAULTS: + { + // Update the channels mask defaults + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); + break; + } default: { break; @@ -558,7 +564,7 @@ bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->Window == 0 ) + if( rxConfig->RxSlot == 0 ) { // Apply window 1 frequency frequency = Channels[rxConfig->Channel].Frequency; diff --git a/lib/lora/mac/region/RegionUS915-Hybrid.c b/lib/lora/mac/region/RegionUS915-Hybrid.c index 06ca70dad1..a13915ef3b 100644 --- a/lib/lora/mac/region/RegionUS915-Hybrid.c +++ b/lib/lora/mac/region/RegionUS915-Hybrid.c @@ -168,7 +168,7 @@ static bool ValidateChannelsMask( uint16_t* channelsMask ) block1 = channelsMaskCpy[i] & 0x00FF; block2 = channelsMaskCpy[i] & 0xFF00; - if( CountBits( block1, 16 ) > 5 ) + if( CountBits( block1, 16 ) > 1 ) { channelsMaskCpy[i] &= block1; channelsMaskCpy[4] = 1 << ( i * 2 ); @@ -176,7 +176,7 @@ static bool ValidateChannelsMask( uint16_t* channelsMask ) index = i; break; } - else if( CountBits( block2, 16 ) > 5 ) + else if( CountBits( block2, 16 ) > 1 ) { channelsMaskCpy[i] &= block2; channelsMaskCpy[4] = 1 << ( i * 2 + 1 ); @@ -431,6 +431,16 @@ void RegionUS915HybridInitDefaults( InitType_t type ) { // Copy-And the channels mask ChannelsMaskRemaining[i] &= ChannelsMask[i]; } + break; + } + case INIT_TYPE_APP_DEFAULTS: + { + // Copy channels default mask + RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 ); + break; } default: { @@ -609,7 +619,7 @@ bool RegionUS915HybridRxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->Window == 0 ) + if( rxConfig->RxSlot == 0 ) { // Apply window 1 frequency frequency = US915_HYBRID_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_HYBRID_STEPWIDTH_RX1_CHANNEL; diff --git a/lib/lora/mac/region/RegionUS915.c b/lib/lora/mac/region/RegionUS915.c index aa5b8bd91e..76c41ce2ce 100644 --- a/lib/lora/mac/region/RegionUS915.c +++ b/lib/lora/mac/region/RegionUS915.c @@ -522,7 +522,7 @@ bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->Window == 0 ) + if( rxConfig->RxSlot == 0 ) { // Apply window 1 frequency frequency = US915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_STEPWIDTH_RX1_CHANNEL; From 581bbc085945509b9b89bc3434dbad03bfa27c58 Mon Sep 17 00:00:00 2001 From: iwahdan88 Date: Thu, 8 Aug 2019 12:08:38 +0200 Subject: [PATCH 06/15] [PYFW-348] updated rx buff size max size exception message --- esp32/mods/machuart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp32/mods/machuart.c b/esp32/mods/machuart.c index 9ad7517528..1618bb8aa5 100644 --- a/esp32/mods/machuart.c +++ b/esp32/mods/machuart.c @@ -379,7 +379,7 @@ STATIC mp_obj_t mach_uart_init_helper(mach_uart_obj_t *self, const mp_arg_val_t int rx_buffer_size = args[6].u_int; // Check is needed because return value of uart_driver_install is not checked if(!(rx_buffer_size > UART_FIFO_LEN)) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "invalid RX buffer size, minimum is 128")); + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "invalid RX buffer size, should be > 128 bytes")); } if (self->config.baud_rate > 0) { From 4d0ff6cc11514cc48ad80b396e9116f16a14f6c3 Mon Sep 17 00:00:00 2001 From: iwahdan88 Date: Thu, 8 Aug 2019 18:17:37 +0200 Subject: [PATCH 07/15] [PYFW-356] support for sending bytes in lte.send_at_cmd() #close --- esp32/lte/lteppp.c | 16 +++++---------- esp32/lte/lteppp.h | 3 +-- esp32/mods/modlte.c | 50 ++++++++++++++++++--------------------------- 3 files changed, 26 insertions(+), 43 deletions(-) diff --git a/esp32/lte/lteppp.c b/esp32/lte/lteppp.c index 2e0523d941..71f400094c 100644 --- a/esp32/lte/lteppp.c +++ b/esp32/lte/lteppp.c @@ -85,7 +85,7 @@ static ip_addr_t ltepp_dns_info[2]={0}; DECLARE PRIVATE FUNCTIONS ******************************************************************************/ static void TASK_LTE (void *pvParameters); -static bool lteppp_send_at_cmd_exp(const char *cmd, uint32_t timeout, const char *expected_rsp, void* data_rem); +static bool lteppp_send_at_cmd_exp(const char *cmd, uint32_t timeout, const char *expected_rsp, void* data_rem, size_t len); static bool lteppp_send_at_cmd(const char *cmd, uint32_t timeout); static bool lteppp_check_sim_present(void); static void lteppp_status_cb (ppp_pcb *pcb, int err_code, void *ctx); @@ -225,12 +225,6 @@ void lteppp_send_at_command (lte_task_cmd_data_t *cmd, lte_task_rsp_data_t *rsp) xQueueReceive(xRxQueue, rsp, (TickType_t)portMAX_DELAY); } -void lteppp_send_at_command_delay (lte_task_cmd_data_t *cmd, lte_task_rsp_data_t *rsp, TickType_t delay) { - xQueueSend(xCmdQueue, (void *)cmd, (TickType_t)portMAX_DELAY); - vTaskDelay(delay); - xQueueReceive(xRxQueue, rsp, (TickType_t)portMAX_DELAY); -} - bool lteppp_wait_at_rsp (const char *expected_rsp, uint32_t timeout, bool from_mp, void* data_rem) { uint32_t rx_len = 0; @@ -514,7 +508,7 @@ static void TASK_LTE (void *pvParameters) { } xSemaphoreGive(xLTESem); if (xQueueReceive(xCmdQueue, lteppp_trx_buffer, 0)) { - lteppp_send_at_cmd_exp(lte_task_cmd->data, lte_task_cmd->timeout, NULL, &(lte_task_rsp->data_remaining)); + lteppp_send_at_cmd_exp(lte_task_cmd->data, lte_task_cmd->timeout, NULL, &(lte_task_rsp->data_remaining), lte_task_cmd->dataLen); xQueueSend(xRxQueue, (void *)lte_task_rsp, (TickType_t)portMAX_DELAY); } else { lte_state_t state = lteppp_get_state(); @@ -555,7 +549,7 @@ static void TASK_LTE (void *pvParameters) { } -static bool lteppp_send_at_cmd_exp (const char *cmd, uint32_t timeout, const char *expected_rsp, void* data_rem) { +static bool lteppp_send_at_cmd_exp (const char *cmd, uint32_t timeout, const char *expected_rsp, void* data_rem, size_t len) { if(strstr(cmd, "Pycom_Dummy") != NULL) { @@ -577,7 +571,7 @@ static bool lteppp_send_at_cmd_exp (const char *cmd, uint32_t timeout, const cha } else { - uint32_t cmd_len = strlen(cmd); + size_t cmd_len = len; // char tmp_buf[128]; #ifdef LTE_DEBUG_BUFF if (lteppp_log.ptr < (LTE_LOG_BUFF_SIZE - strlen("[CMD]:") - cmd_len + 1)) @@ -611,7 +605,7 @@ static bool lteppp_send_at_cmd_exp (const char *cmd, uint32_t timeout, const cha } static bool lteppp_send_at_cmd(const char *cmd, uint32_t timeout) { - return lteppp_send_at_cmd_exp (cmd, timeout, LTE_OK_RSP, NULL); + return lteppp_send_at_cmd_exp (cmd, timeout, LTE_OK_RSP, NULL, strlen(cmd) ); } static bool lteppp_check_sim_present(void) { diff --git a/esp32/lte/lteppp.h b/esp32/lte/lteppp.h index 9977aa9765..1679da9b22 100644 --- a/esp32/lte/lteppp.h +++ b/esp32/lte/lteppp.h @@ -72,6 +72,7 @@ typedef struct { typedef struct { uint32_t timeout; char data[LTE_AT_CMD_SIZE_MAX - 4]; + size_t dataLen; } lte_task_cmd_data_t; #pragma pack(1) typedef struct { @@ -111,8 +112,6 @@ extern void lteppp_deinit (void); extern void lteppp_send_at_command (lte_task_cmd_data_t *cmd, lte_task_rsp_data_t *rsp); -extern void lteppp_send_at_command_delay (lte_task_cmd_data_t *cmd, lte_task_rsp_data_t *rsp, TickType_t delay); - extern bool lteppp_wait_at_rsp (const char *expected_rsp, uint32_t timeout, bool from_mp, void* data_rem); lte_modem_conn_state_t lteppp_modem_state(void); diff --git a/esp32/mods/modlte.c b/esp32/mods/modlte.c index f171b8afb6..b434201912 100644 --- a/esp32/mods/modlte.c +++ b/esp32/mods/modlte.c @@ -116,7 +116,7 @@ extern TaskHandle_t xLTETaskHndl; /****************************************************************************** DECLARE PRIVATE FUNCTIONS ******************************************************************************/ -static bool lte_push_at_command_ext (char *cmd_str, uint32_t timeout, const char *expected_rsp); +static bool lte_push_at_command_ext (char *cmd_str, uint32_t timeout, const char *expected_rsp, size_t len); static bool lte_push_at_command (char *cmd_str, uint32_t timeout); static void lte_pause_ppp(void); static bool lte_check_attached(bool legacy); @@ -146,25 +146,12 @@ void modlte_start_modem(void) // DEFINE STATIC FUNCTIONS //***************************************************************************** -static bool lte_push_at_command_ext(char *cmd_str, uint32_t timeout, const char *expected_rsp) { - lte_task_cmd_data_t cmd = { .timeout = timeout }; - memcpy(cmd.data, cmd_str, strlen(cmd_str)); +static bool lte_push_at_command_ext(char *cmd_str, uint32_t timeout, const char *expected_rsp, size_t len) { + lte_task_cmd_data_t cmd = { .timeout = timeout, .dataLen = len}; + memcpy(cmd.data, cmd_str, len); //printf("[CMD] %s\n", cmd_str); lteppp_send_at_command(&cmd, &modlte_rsp); - if (strstr(modlte_rsp.data, expected_rsp) != NULL) { - //printf("[OK] %s\n", modlte_rsp.data); - return true; - } - //printf("[FAIL] %s\n", modlte_rsp.data); - return false; -} - -static bool lte_push_at_command_delay_ext (char *cmd_str, uint32_t timeout, const char *expected_rsp, TickType_t delay) { - lte_task_cmd_data_t cmd = { .timeout = timeout }; - memcpy(cmd.data, cmd_str, strlen(cmd_str)); - //printf("[CMD] %s\n", cmd_str); - lteppp_send_at_command_delay(&cmd, &modlte_rsp, delay); - if (strstr(modlte_rsp.data, expected_rsp) != NULL) { + if ((expected_rsp == NULL) || (strstr(modlte_rsp.data, expected_rsp) != NULL)) { //printf("[OK] %s\n", modlte_rsp.data); return true; } @@ -173,11 +160,7 @@ static bool lte_push_at_command_delay_ext (char *cmd_str, uint32_t timeout, cons } static bool lte_push_at_command (char *cmd_str, uint32_t timeout) { - return lte_push_at_command_ext(cmd_str, timeout, LTE_OK_RSP); -} - -static bool lte_push_at_command_delay (char *cmd_str, uint32_t timeout, TickType_t delay) { - return lte_push_at_command_delay_ext(cmd_str, timeout, LTE_OK_RSP, delay); + return lte_push_at_command_ext(cmd_str, timeout, LTE_OK_RSP, strlen(cmd_str)); } static void lte_pause_ppp(void) { @@ -969,14 +952,14 @@ STATIC mp_obj_t lte_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t } if (lteppp_get_state() == E_LTE_ATTACHED || (args[1].u_bool && lteppp_get_state() == E_LTE_SUSPENDED)) { - if (args[1].u_bool || !lte_push_at_command_ext("ATO", LTE_RX_TIMEOUT_MAX_MS, LTE_CONNECT_RSP)) { + if (args[1].u_bool || !lte_push_at_command_ext("ATO", LTE_RX_TIMEOUT_MAX_MS, LTE_CONNECT_RSP, strlen("ATO") )) { char at_cmd[LTE_AT_CMD_SIZE_MAX - 4]; if (args[0].u_obj != mp_const_none) { lte_obj.cid = args[0].u_int; } sprintf(at_cmd, "AT+CGDATA=\"PPP\",%d", lte_obj.cid); // set the PPP state in advance, to avoid CEREG? to be sent right after PPP is entered - if (!lte_push_at_command_ext(at_cmd, LTE_RX_TIMEOUT_MAX_MS, LTE_CONNECT_RSP)) { + if (!lte_push_at_command_ext(at_cmd, LTE_RX_TIMEOUT_MAX_MS, LTE_CONNECT_RSP, strlen(at_cmd) )) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_operation_failed)); } } @@ -1019,7 +1002,7 @@ STATIC mp_obj_t lte_resume(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t lte_obj.cid = args[0].u_int; } - if (lte_push_at_command_ext("ATO", LTE_RX_TIMEOUT_MAX_MS, LTE_CONNECT_RSP)) { + if (lte_push_at_command_ext("ATO", LTE_RX_TIMEOUT_MAX_MS, LTE_CONNECT_RSP, strlen("ATO") )) { lteppp_connect(); lteppp_resume(); lteppp_set_state(E_LTE_PPP); @@ -1090,7 +1073,6 @@ STATIC mp_obj_t lte_send_at_cmd(mp_uint_t n_args, const mp_obj_t *pos_args, mp_m lte_check_inppp(); STATIC const mp_arg_t allowed_args[] = { { MP_QSTR_cmd, MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_delay, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, }; // parse args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -1098,15 +1080,23 @@ STATIC mp_obj_t lte_send_at_cmd(mp_uint_t n_args, const mp_obj_t *pos_args, mp_m if (args[0].u_obj == mp_const_none) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "the command must be specified!")); } - const char *cmd = mp_obj_str_get_str(args[0].u_obj); - lte_push_at_command_delay((char *)cmd, LTE_RX_TIMEOUT_MAX_MS, args[1].u_int); + if (MP_OBJ_IS_STR_OR_BYTES(args[0].u_obj)) + { + size_t len; + lte_push_at_command_ext((char *)(mp_obj_str_get_data(args[0].u_obj, &len)), LTE_RX_TIMEOUT_MAX_MS, NULL, len); + } + else + { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, mpexception_num_type_invalid_arguments)); + } + vstr_t vstr; vstr_init(&vstr, 0); vstr_add_str(&vstr, modlte_rsp.data); MP_THREAD_GIL_EXIT(); while(modlte_rsp.data_remaining) { - lte_push_at_command_delay("Pycom_Dummy", LTE_RX_TIMEOUT_MAX_MS, args[1].u_int); + lte_push_at_command_ext("Pycom_Dummy", LTE_RX_TIMEOUT_MAX_MS, NULL, strlen("Pycom_Dummy") ); vstr_add_str(&vstr, modlte_rsp.data); } MP_THREAD_GIL_ENTER(); From 0838babfe4fa1076c944da44ec55cb658061e03f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9za=20Husi?= Date: Wed, 14 Aug 2019 11:56:59 +0200 Subject: [PATCH 08/15] PYFW-359: When building SW with "BTYPE=debug" littleFS assertion fails with crash (#51) --- esp32/littlefs/sflash_diskio_littlefs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esp32/littlefs/sflash_diskio_littlefs.c b/esp32/littlefs/sflash_diskio_littlefs.c index d8a8175aae..f386cf47f4 100644 --- a/esp32/littlefs/sflash_diskio_littlefs.c +++ b/esp32/littlefs/sflash_diskio_littlefs.c @@ -9,7 +9,8 @@ char prog_buffer[SFLASH_BLOCK_SIZE] = {0}; char read_buffer[SFLASH_BLOCK_SIZE] = {0}; -char lookahead_buffer[SFLASH_BLOCK_COUNT_8MB/8] = {0}; +// Must be on 64 bit aligned address, create it as array of 64 bit entries to achieve it +uint64_t lookahead_buffer[SFLASH_BLOCK_COUNT_8MB/(8*8)] = {0}; int littlefs_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) { From 04e21d94a213830a92cd6e3a7004e03bc6bdc165 Mon Sep 17 00:00:00 2001 From: iwahdan88 Date: Wed, 14 Aug 2019 19:48:24 +0200 Subject: [PATCH 09/15] Added API to get Internal and External Free RAM size --- esp32/mods/modpycom.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/esp32/mods/modpycom.c b/esp32/mods/modpycom.c index 5160d35aa5..94012da8a9 100644 --- a/esp32/mods/modpycom.c +++ b/esp32/mods/modpycom.c @@ -478,6 +478,30 @@ STATIC mp_obj_t mod_pycom_bootmgr (size_t n_args, const mp_obj_t *pos_args, mp_m STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_pycom_bootmgr_obj, 0, mod_pycom_bootmgr); +STATIC mp_obj_t mod_pycom_get_free_heap (void) { + + size_t heap_psram_free = 0; + mp_obj_t items[2]; + esp_chip_info_t chip_info; + + esp_chip_info(&chip_info); + if (chip_info.revision > 0) { + heap_psram_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } + items[0] = mp_obj_new_int(heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); + if(heap_psram_free) + { + items[1] = mp_obj_new_int(heap_psram_free); + } + else + { + items[1] = mp_obj_new_str("NO_EXT_RAM", strlen("NO_EXT_RAM")); + } + + return mp_obj_new_tuple(2, items); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_pycom_get_free_heap_obj, mod_pycom_get_free_heap); + #if (VARIANT == PYBYTES) STATIC mp_obj_t mod_pycom_pybytes_device_token (void) { uint8_t pybytes_device_token[39]; @@ -546,6 +570,7 @@ STATIC const mp_map_elem_t pycom_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_wifi_pwd), (mp_obj_t)&mod_pycom_wifi_pwd_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_heartbeat_on_boot), (mp_obj_t)&mod_pycom_heartbeat_on_boot_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_lte_modem_en_on_boot), (mp_obj_t)&mod_pycom_lte_modem_on_boot_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_get_free_heap), (mp_obj_t)&mod_pycom_get_free_heap_obj }, #if (VARIANT == PYBYTES) { MP_OBJ_NEW_QSTR(MP_QSTR_pybytes_device_token), (mp_obj_t)&mod_pycom_pybytes_device_token_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_pybytes_mqttServiceAddress), (mp_obj_t)&mod_pycom_pybytes_mqttServiceAddress_obj }, From 0048418fc4a552197c96e99ac68a12ce47bbea96 Mon Sep 17 00:00:00 2001 From: iwahdan88 Date: Wed, 14 Aug 2019 19:52:30 +0200 Subject: [PATCH 10/15] Updated Makfile --- esp32/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp32/Makefile b/esp32/Makefile index 4f2efa8b89..0468ea9da6 100644 --- a/esp32/Makefile +++ b/esp32/Makefile @@ -130,7 +130,7 @@ endif # Enable or Disable LTE_LOG_BUFF ifeq ($(BOARD), $(filter $(BOARD), GPY FIPY)) -ifeq ($(LTE_DEBUG_BUFF),1) +ifeq ($(LTE_LOG_BUFF),1) CFLAGS += -DLTE_DEBUG_BUFF endif #ifeq ($(LTE_LOG_BUFF),1) endif #ifeq ($(BOARD), $(filter $(BOARD), GPY FIPY)) From b09f59b96acfa3ba4c0684bdc92a20d9363c7d5e Mon Sep 17 00:00:00 2001 From: iwahdan88 Date: Tue, 20 Aug 2019 18:14:34 +0200 Subject: [PATCH 11/15] Updated Pybytes version to v1.0.0 --- esp32/frozen/Pybytes/_OTA.py | 8 + esp32/frozen/Pybytes/_flash_control_OTA.py | 9 + esp32/frozen/Pybytes/_mqtt.py | 140 +++-- esp32/frozen/Pybytes/_mqtt_core.py | 620 +++++++++++++------- esp32/frozen/Pybytes/_msg_handl.py | 258 ++++++++ esp32/frozen/Pybytes/_pybytes.py | 10 + esp32/frozen/Pybytes/_pybytes_ca.py | 8 + esp32/frozen/Pybytes/_pybytes_config.py | 8 + esp32/frozen/Pybytes/_pybytes_connection.py | 166 ++++-- esp32/frozen/Pybytes/_pybytes_constants.py | 52 +- esp32/frozen/Pybytes/_pybytes_debug.py | 61 +- esp32/frozen/Pybytes/_pybytes_library.py | 9 + esp32/frozen/Pybytes/_pybytes_protocol.py | 268 +++++---- esp32/frozen/Pybytes/_terminal.py | 18 +- 14 files changed, 1189 insertions(+), 446 deletions(-) create mode 100644 esp32/frozen/Pybytes/_msg_handl.py diff --git a/esp32/frozen/Pybytes/_OTA.py b/esp32/frozen/Pybytes/_OTA.py index d772a680c9..532f3c6773 100644 --- a/esp32/frozen/Pybytes/_OTA.py +++ b/esp32/frozen/Pybytes/_OTA.py @@ -1,3 +1,11 @@ +''' +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + import network import socket import ssl diff --git a/esp32/frozen/Pybytes/_flash_control_OTA.py b/esp32/frozen/Pybytes/_flash_control_OTA.py index 1cacf71980..e1a70cd31e 100644 --- a/esp32/frozen/Pybytes/_flash_control_OTA.py +++ b/esp32/frozen/Pybytes/_flash_control_OTA.py @@ -1,9 +1,18 @@ +''' +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + import os try: from pybytes_debug import print_debug except: from _pybytes_debug import print_debug + class FCOTA: def __init__(self): pass diff --git a/esp32/frozen/Pybytes/_mqtt.py b/esp32/frozen/Pybytes/_mqtt.py index 395f021209..609a612be4 100644 --- a/esp32/frozen/Pybytes/_mqtt.py +++ b/esp32/frozen/Pybytes/_mqtt.py @@ -1,112 +1,142 @@ -import time +''' +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + +try: + from mqtt_core import MQTTCore as mqtt_core +except: + from _mqtt_core import MQTTCore as mqtt_core + +try: + from pybytes_constants import MQTTConstants as mqttConst +except: + from _pybytes_constants import MQTTConstants as mqttConst + try: - import mqtt_core + from pybytes_debug import print_debug except: - import _mqtt_core as mqtt_core + from _pybytes_debug import print_debug + +import time class MQTTClient: - def __init__(self, client_id, server, mqtt_download_topic, port=0, user=None, password=None, keepalive=0, ssl=False, - ssl_params={}, reconnect=True): + def __init__( + self, client_id, server, mqtt_download_topic, + pybytes_protocol, port=0, user=None, password=None, + keepalive=0, ssl=False, + ssl_params={}, reconnect=True + ): + if port == 0: + self.__port = 8883 if ssl else 1883 + else: + self.__port = port self.__reconnect = reconnect self.__reconnect_count = 0 self.__reconnecting = False self.__server = server self.__mqtt_download_topic = mqtt_download_topic - self.__mqtt = mqtt_core.MQTTClient(client_id, server, port, user, password, keepalive, - ssl, ssl_params) + self.__pybytes_protocol = pybytes_protocol + self.__clientId = client_id + self.__user = user + self.__password = password + self.init_mqtt_core() + + def init_mqtt_core(self): + self.__mqtt = mqtt_core( + self.__clientId, + True, + mqttConst.MQTTv3_1_1, + receive_timeout=500, + reconnectMethod=self.reconnect + ) + self.__mqtt.configEndpoint(self.__server, self.__port) + self.__mqtt._user = self.__user + self.__mqtt._password = self.__password def getError(self, x): """Return a human readable error instead of its code number""" - # This errors are thrown by connect function, I wouldn't be able to find + # This errors are thrown by connect function, + # I wouldn't be able to find # anywhere a complete list of these error codes ERRORS = { - '-1': 'MQTTClient: Can\'t connect to MQTT server: "{}"'.format(self.__server), - '-4': 'MQTTClient: Bad credentials when connecting to MQTT server: "{}"'.format(self.__server), - '-9984': 'MQTTClient: Invalid certificate validation when connecting to MQTT server: "{}"'.format(self.__server) + '-1': 'MQTTClient: Can\'t connect to MQTT server: "{}"'.format(self.__server), # noqa + '-4': 'MQTTClient: Bad credentials when connecting to MQTT server: "{}"'.format(self.__server), # noqa + '-9984': 'MQTTClient: Invalid certificate validation when connecting to MQTT server: "{}"'.format(self.__server) # noqa } message = str(x) - return ERRORS.get(str(x), 'Unknown error while connecting to MQTT server ' + message) + return ERRORS.get( + str(x), + 'Unknown error while connecting to MQTT server {}'.format(message) + ) def connect(self, clean_session=True): i = 0 while 1: try: - return self.__mqtt.connect(clean_session) - except OSError as e: - print(self.getError(e)) + return self.__mqtt.connect() + except OSError: if (not self.__reconnect): raise Exception('Reconnection Disabled.') i += 1 time.sleep(i) - def set_callback(self, f): - self.__mqtt.set_callback(f) + def set_callback(self, mqtt_client, message): + self.__pybytes_protocol.__process_recv_message(message) def subscribe(self, topic, qos=0): - self.__mqtt.subscribe(topic, qos) - - def check_msg(self): - while 1: - time_before_retry = 10 - if self.__reconnecting == False: - try: - return self.__mqtt.check_msg() - except OSError as e: - print("Error check_msg", e) - - if (not self.__reconnect): - raise Exception('Reconnection Disabled.') - self.reconnect() - break - else: - time.sleep(time_before_retry) + self.__mqtt.subscribe(topic, qos, self.set_callback) + def unsubscribe(self, topic): + return self.__mqtt.unsubscribe(topic) def reconnect(self): if self.__reconnecting: return + + self.init_mqtt_core() + while True: - self.__reconnect_count += 1 + self.__reconnect_count += 1 self.__reconnecting = True try: - self.__mqtt.connect() + if not self.__mqtt.connect(): + time.sleep(self.__reconnect_count) + continue self.subscribe(self.__mqtt_download_topic) - self.__reconnect_count=0 - print('Reconnected to MQTT server: "{}"'.format(self.__server)) + self.__reconnect_count = 0 self.__reconnecting = False break except OSError: - print("Reconnecting failed, will retry in {} seconds".format(self.__reconnect_count)) time.sleep(self.__reconnect_count) - def publish(self, topic, msg, retain=False, qos=0): + def publish(self, topic, msg, retain=False, qos=0, priority=False): while 1: - if self.__reconnecting == False: + if not self.__reconnecting: try: - return self.__mqtt.publish(topic, msg, retain, qos) + # Disable retain for publish by now + return self.__mqtt.publish( + topic, + msg, + qos, + False, + priority=priority + ) except OSError as e: - print("Error publish", e) + print_debug(2, "Error publish", e) if (not self.__reconnect): raise Exception('Reconnection Disabled.') self.reconnect() - break + raise Exception('Error publish.') else: time.sleep(10) - def wait_msg(self): - while 1: - try: - return self.__mqtt.wait_msg() - except OSError as e: - print("Error wait_msg {}".format(e)) - - if (not self.__reconnect): - raise Exception('Reconnection Disabled.') - self.reconnect() - def disconnect(self): self.__mqtt.disconnect() diff --git a/esp32/frozen/Pybytes/_mqtt_core.py b/esp32/frozen/Pybytes/_mqtt_core.py index d9fa9253e7..c71c2fcfc6 100644 --- a/esp32/frozen/Pybytes/_mqtt_core.py +++ b/esp32/frozen/Pybytes/_mqtt_core.py @@ -1,229 +1,401 @@ -'''umqtt is a simple MQTT client for MicroPython. - Original code: https://github.com/micropython/micropython-lib/tree/master/umqtt.simple''' +''' +umqtt is a simple MQTT client for MicroPython. +Original code: https://github.com/micropython/micropython-lib/tree/master/umqtt.simple + +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + +try: + from msg_handl import MsgHandler as msgHandler +except: + from _msg_handl import MsgHandler as msgHandler + +try: + from pybytes_constants import MQTTConstants as mqttConst +except: + from _pybytes_constants import MQTTConstants as mqttConst +try: + from pybytes_debug import print_debug +except: + from _pybytes_debug import print_debug + import time -import usocket as socket -import ustruct as struct -from ubinascii import hexlify - - -class MQTTException(Exception): - pass - - -class MQTTClient: - - def __init__(self, client_id, server, port=0, user=None, password=None, keepalive=0, - ssl=False, ssl_params={}): - if port == 0: - port = 8883 if ssl else 1883 - self.client_id = client_id - self.sock = None - self.addr = socket.getaddrinfo(server, port)[0][-1] - self.ssl = ssl - self.ssl_params = ssl_params - self.pid = 0 - self.cb = None - self.user = user - self.pswd = password - self.keepalive = keepalive - self.lastWill_topic = None - self.lastWill_msg = None - self.lastWill_qos = 0 - self.lastWill_retain = False - - def _send_str(self, s): - self.sock.write(struct.pack("!H", len(s))) - self.sock.write(s) - - def _recv_len(self): - n = 0 - sh = 0 +import struct +import _thread + + +class MQTTMessage: + def __init__(self): + self.timestamp = 0 + self.state = 0 + self.dup = False + self.mid = 0 + self.topic = "" + self.payload = None + self.qos = 0 + self.retain = False + + +class MQTTCore: + + def __init__( + self, + clientID, + cleanSession, + protocol, + receive_timeout=3000, + reconnectMethod=None + ): + self.client_id = clientID + self._cleanSession = cleanSession + self._protocol = protocol + self._user = "" + self._password = "" + self._connectdisconnectTimeout = 30 + self._mqttOperationTimeout = 5 + self._topic_callback_queue = [] + self._callback_mutex = _thread.allocate_lock() + self._pid = 0 + self._subscribeSent = False + self._unsubscribeSent = False + self._msgHandler = msgHandler( + self._recv_callback, + self.connect, + receive_timeout, + reconnectMethod + ) + + def configEndpoint(self, srcHost, srcPort): + self._msgHandler.setEndpoint(srcHost, srcPort) + + def connect(self): + if not self._msgHandler.createSocketConnection(): + return False + + self._send_connect(self._cleanSession) + + # delay to check the state + count_10ms = 0 + while(count_10ms <= self._connectdisconnectTimeout * 100 and not self._msgHandler.isConnected()): # noqa + count_10ms += 1 + time.sleep(0.01) + + return True if self._msgHandler.isConnected() else False + + def subscribe(self, topic, qos, callback): + if (topic is None or callback is None): + raise TypeError("Invalid subscribe values.") + topic = topic.encode('utf-8') + + header = mqttConst.MSG_SUBSCRIBE | (1 << 1) + pkt = bytearray([header]) + + # packet identifier + len of topic (16 bits) + topic len + QOS + pkt_len = 2 + 2 + len(topic) + 1 + pkt.extend(self._encode_varlen_length(pkt_len)) # len of the remaining + + self._pid += 1 + pkt.extend(self._encode_16(self._pid)) + pkt.extend(self._pascal_string(topic)) + pkt.append(qos) + + self._subscribeSent = False + self._msgHandler.push_on_send_queue(pkt) + + count_10ms = 0 + while(count_10ms <= self._mqttOperationTimeout * 100 and not self._subscribeSent): # noqa + count_10ms += 1 + time.sleep(0.01) + + if self._subscribeSent: + self._callback_mutex.acquire() + self._topic_callback_queue.append((topic, callback)) + self._callback_mutex.release() + return True + + return False + + def publish(self, topic, payload, qos, retain, dup=False, priority=False): + topic = topic.encode('utf-8') if hasattr(topic, 'encode') else topic + payload = payload.encode('utf-8') if hasattr(payload, 'encode') else payload # noqa + + header = mqttConst.MSG_PUBLISH | (dup << 3) | (qos << 1) | retain + pkt_len = (2 + len(topic) + (2 if qos else 0) + (len(payload))) + + pkt = bytearray([header]) + pkt.extend(self._encode_varlen_length(pkt_len)) # len of the remaining + pkt.extend(self._pascal_string(topic)) + if qos: + # todo: I don't think this is the way to deal with the packet id + self._pid += 1 + pkt.extend(self._encode_16(self._pid)) + + pkt = pkt + payload + if priority: + self._msgHandler.priority_send(pkt) + else: + self._msgHandler.push_on_send_queue(pkt) + + def _encode_16(self, x): + return struct.pack("!H", x) + + def _pascal_string(self, s): + return struct.pack("!H", len(s)) + s + + def _encode_varlen_length(self, length): + i = 0 + buff = bytearray() while 1: - b = self.sock.read(1)[0] - n |= (b & 0x7f) << sh - if not b & 0x80: - return n - sh += 7 - - def set_callback(self, f): - self.cb = f - - def set_last_will(self, topic, msg, retain=False, qos=0): - assert 0 <= qos <= 2 - assert topic - self.lastWill_topic = topic - self.lastWill_msg = msg - self.lastWill_qos = qos - self.lastWill_retain = retain - - def connect(self, clean_session=True): - if (self.sock): - # https://pycomiot.atlassian.net/browse/PB-358 - try: - self.sock.send('') # can send == closeable - self.sock.close() - except Exception as e: - self.sock = None # socket is not closable - import gc - gc.collect() - - self.sock = socket.socket() - self.sock.connect(self.addr) - if self.ssl == True: - import ussl - if self.ssl_params != None and self.ssl_params.get('ca_certs'): - self.ssl_params["cert_reqs"] = ussl.CERT_REQUIRED + buff.append(length % 128) + length = length // 128 + if length > 0: + buff[i] = buff[i] | 0x80 + i += 1 else: - print("WARNING: consider enabling certificate validation") - self.sock = ussl.wrap_socket(self.sock, **self.ssl_params) - print("Using MQTT over TLS") - else: - print("WARNING: consider enabling TLS by using pybytes.enable_ssl()") - premsg = bytearray(b"\x10\0\0\0\0\0") - msg = bytearray(b"\x04MQTT\x04\x02\0\0") - - size = 10 + 2 + len(self.client_id) - msg[6] = clean_session << 1 - if self.user is not None: - size += 2 + len(self.user) + 2 + len(self.pswd) - msg[6] |= 0xC0 - if self.keepalive: - assert self.keepalive < 65536 - msg[7] |= self.keepalive >> 8 - msg[8] |= self.keepalive & 0x00FF - if self.lastWill_topic: - size += 2 + len(self.lastWill_topic) + 2 + len(self.lastWill_msg) - msg[6] |= 0x4 | (self.lastWill_qos & 0x1) << 3 | (self.lastWill_qos & 0x2) << 3 - msg[6] |= self.lastWill_retain << 5 - - i = 1 - while size > 0x7f: - premsg[i] = (size & 0x7f) | 0x80 - size >>= 7 - i += 1 - premsg[i] = size - - self.sock.write(premsg, i + 2) - self.sock.write(msg) - self._send_str(self.client_id) - - if self.lastWill_topic: - self._send_str(self.lastWill_topic) - self._send_str(self.lastWill_msg) - - if self.user is not None: - self._send_str(self.user) - self._send_str(self.pswd) - - resp = self.sock.read(4) - assert resp[0] == 0x20 and resp[1] == 0x02 - if resp[3] != 0: - raise MQTTException(resp[3]) - return resp[2] & 1 + break + + return buff + + def _topic_matches_sub(self, sub, topic): + result = True + multilevel_wildcard = False + + slen = len(sub) + tlen = len(topic) + + if slen > 0 and tlen > 0: + if (sub[0] == '$' and topic[0] != '$') or (topic[0] == '$' and sub[0] != '$'): # noqa + return False + + spos = 0 + tpos = 0 + + while spos < slen and tpos < tlen: + if sub[spos] == topic[tpos]: + if tpos == tlen-1: + # Check for e.g. foo matching foo/# + if spos == slen-3 and sub[spos+1] == '/' and sub[spos+2] == '#': # noqa + result = True + multilevel_wildcard = True + break + + spos += 1 + tpos += 1 + + if tpos == tlen and spos == slen-1 and sub[spos] == '+': + spos += 1 + result = True + break + else: + if sub[spos] == '+': + spos += 1 + while tpos < tlen and topic[tpos] != '/': + tpos += 1 + if tpos == tlen and spos == slen: + result = True + break + + elif sub[spos] == '#': + multilevel_wildcard = True + if spos+1 != slen: + result = False + break + else: + result = True + break + + else: + result = False + break + + if not multilevel_wildcard and (tpos < tlen or spos < slen): + result = False + + return result + + def _remove_topic_callback(self, topic): + deleted = False + + self._callback_mutex.acquire() + for i in range(0, len(self._topic_callback_queue)): + if self._topic_callback_queue[i][0] == topic: + self._topic_callback_queue.pop(i) + deleted = True + self._callback_mutex.release() + + return deleted + + def unsubscribe(self, topic): + self._unsubscribeSent = False + self._send_unsubscribe(topic, False) + + count_10ms = 0 + while(count_10ms <= self._mqttOperationTimeout * 100 and not self._unsubscribeSent): # noqa + count_10ms += 1 + time.sleep(0.01) + + if self._unsubscribeSent: + topic = topic.encode('utf-8') + return self._remove_topic_callback(topic) + + return False def disconnect(self): - self.sock.write(b"\xe0\0") - self.sock.close() - - def ping(self): - self.sock.write(b"\xc0\0") - - def publish(self, topic, msg, retain=False, qos=0): - pkt = bytearray(b"\x30\0\0\0") - pkt[0] |= qos << 1 | retain - size = 2 + len(topic) + len(msg) - if qos > 0: - size += 2 - assert size < 2097152 - i = 1 - while size > 0x7f: - pkt[i] = (size & 0x7f) | 0x80 - size >>= 7 - i += 1 - pkt[i] = size - # print('publish', hex(len(pkt)), hexlify(pkt).decode('ascii')) - self.sock.write(pkt, i + 1) - self._send_str(topic) - if qos > 0: - self.pid += 1 - pid = self.pid - struct.pack_into("!H", pkt, 0, pid) - self.sock.write(pkt, 2) - self.sock.write(msg) - - if qos == 1: - while 1: - op = self.wait_msg() - if op == 0x40: - size = self.sock.read(1) - assert size == b"\x02" - rcv_pid = self.sock.read(2) - rcv_pid = rcv_pid[0] << 8 | rcv_pid[1] - if pid == rcv_pid: - return - elif qos == 2: - assert 0 - - def subscribe(self, topic, qos=0): - assert self.cb is not None, "Subscribe callback is not set" - pkt = bytearray(b"\x82\0\0\0") - self.pid += 1 - struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid) - # print('subscribe', hex(len(pkt)), hexlify(pkt).decode('ascii')) - self.sock.write(pkt) - self._send_str(topic) - self.sock.write(qos.to_bytes(1, "little")) - while 1: - op = self.wait_msg() - if op == 0x90: - resp = self.sock.read(4) - # print('Response subscribe', hexlify(resp).decode('ascii')) - assert resp[1] == pkt[2] and resp[2] == pkt[3] - if resp[3] == 0x80: - raise MQTTException(resp[3]) - return - - # Wait for a single incoming MQTT message and process it. - # Subscribed messages are delivered to a callback previously - # set by .set_callback() method. Other (internal) MQTT - # messages processed internally. - def wait_msg(self): - res = self.sock.read(1) - self.sock.setblocking(True) - if res is None: - return None - if res == b"": - # other tls empty response happens ... - # raise OSError(-1) - return - if res == b"\xd0": # PING RESPONCE - size = self.sock.read(1)[0] - assert size == 0 - return None - op = res[0] - if op & 0xf0 != 0x30: - return op - size = self._recv_len() - topic_len = self.sock.read(2) - topic_len = (topic_len[0] << 8) | topic_len[1] - topic = self.sock.read(topic_len) - size -= topic_len + 2 - if op & 6: - pid = self.sock.read(2) - pid = pid[0] << 8 | pid[1] - size -= 2 - msg = self.sock.read(size) - self.cb(topic, msg) - if op & 6 == 2: - pkt = bytearray(b"\x40\x02\0\0") - struct.pack_into("!H", pkt, 2, pid) - self.sock.write(pkt) - elif op & 6 == 4: - assert 0 - - # Checks whether a pending message from server is available. - # If not, returns immediately with None. Otherwise, does - # the same processing as wait_msg. - def check_msg(self): - self.sock.setblocking(False) - return self.wait_msg() + pkt = struct.pack('!BB', mqttConst.MSG_DISCONNECT, 0) + self._msgHandler.push_on_send_queue(pkt) + time.sleep(self._connectdisconnectTimeout) + self._msgHandler.disconnect() + return True + + def _send_connect(self, clean_session): + pkt_len = (12 + len(self.client_id) + # 10 + 2 + len(client_id) + (2 + len(self._user) if self._user else 0) + + (2 + len(self._password) if self._password else 0)) + + flags = (0x80 if self._user else 0x00) | (0x40 if self._password else 0x00) | (0x02 if clean_session else 0x00) # noqa + + pkt = bytearray([mqttConst.MSG_CONNECT]) # connect + pkt.extend(self._encode_varlen_length(pkt_len)) # len of the remaining + # len of "MQTT" (16 bits), protocol name, and protocol version + pkt.extend(b'\x00\x04MQTT\x04') + pkt.append(flags) + pkt.extend(b'\x00\x00') # disable keepalive + pkt.extend(self._pascal_string(self.client_id)) + + if self._user: + pkt.extend(self._pascal_string(self._user)) + if self._password: + pkt.extend(self._pascal_string(self._password)) + + return self._msgHandler.priority_send(pkt) + + def _send_unsubscribe(self, topic, dup=False): + pkt = bytearray() + msg_type = mqttConst.MSG_UNSUBSCRIBE | (dup << 3) | (1 << 1) + pkt.extend(struct.pack("!B", msg_type)) + + remaining_length = 2 + 2 + len(topic) + pkt.extend(self._encode_varlen_length(remaining_length)) + + self._pid += 1 + pkt.extend(self._encode_16(self._pid)) + pkt.extend(self._pascal_string(topic)) + + return self._msgHandler.push_on_send_queue(pkt) + + def _send_puback(self, msg_id): + remaining_length = 2 + pkt = struct.pack( + '!BBH', + mqttConst.MSG_PUBACK, + remaining_length, + msg_id + ) + + return self._msgHandler.push_on_send_queue(pkt) + + def _send_pubrec(self, msg_id): + remaining_length = 2 + pkt = struct.pack( + '!BBH', + mqttConst.MSG_PUBREC, + remaining_length, + msg_id + ) + + return self._msgHandler.push_on_send_queue(pkt) + + def _parse_connack(self, payload): + if len(payload) != 2: + return False + + (flags, result) = struct.unpack("!BB", payload) + + if result == 0: + self._msgHandler.setConnectionState(mqttConst.STATE_CONNECTED) + return True + else: + self._msgHandler.setConnectionState(mqttConst.STATE_DISCONNECTED) + return False + + def _parse_suback(self, payload): + self._subscribeSent = True + return True + + def _parse_puback(self, payload): + return True + + def _notify_message(self, message): + notified = False + self._callback_mutex.acquire() + for t_obj in self._topic_callback_queue: + if self._topic_matches_sub(t_obj[0], message.topic): + t_obj[1](self, message) + notified = True + self._callback_mutex.release() + + return notified + + def _parse_publish(self, cmd, packet): + msg = MQTTMessage() + msg.dup = (cmd & 0x08) >> 3 + msg.qos = (cmd & 0x06) >> 1 + msg.retain = (cmd & 0x01) + + pack_format = "!H" + str(len(packet)-2) + 's' + (slen, packet) = struct.unpack(pack_format, packet) + pack_format = '!' + str(slen) + 's' + str(len(packet)-slen) + 's' + (msg.topic, packet) = struct.unpack(pack_format, packet) + + if len(msg.topic) == 0: + return False + + if msg.qos > 0: + pack_format = "!H" + str(len(packet)-2) + 's' + (msg.mid, packet) = struct.unpack(pack_format, packet) + + msg.payload = packet + + if msg.qos == 0: + self._notify_message(msg) + elif msg.qos == 1: + self._send_puback(msg.mid) + self._notify_message(msg) + elif msg.qos == 2: + self._send_pubrec(msg.mid) + self._notify_message(msg) + else: + return False + + return True + + def _parse_unsuback(self, payload): + self._unsubscribeSent = True + return True + + def _parse_pingresp(self): + self._msgHandler.setPingFlag(True) + return True + + def _recv_callback(self, cmd, payload): + msg_type = cmd & 0xF0 + + if msg_type == mqttConst.MSG_CONNACK: + return self._parse_connack(payload) + elif msg_type == mqttConst.MSG_SUBACK: + return self._parse_suback(payload) + elif msg_type == mqttConst.MSG_PUBACK: + return self._parse_puback(payload) + elif msg_type == mqttConst.MSG_PUBLISH: + return self._parse_publish(cmd, payload) + elif msg_type == mqttConst.MSG_UNSUBACK: + return self._parse_unsuback(payload) + elif msg_type == mqttConst.MSG_PINGRESP: + return self._parse_pingresp() + else: + print_debug(2, 'Unknown message type: %d' % msg_type) + return False diff --git a/esp32/frozen/Pybytes/_msg_handl.py b/esp32/frozen/Pybytes/_msg_handl.py new file mode 100644 index 0000000000..0047a96084 --- /dev/null +++ b/esp32/frozen/Pybytes/_msg_handl.py @@ -0,0 +1,258 @@ +''' +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + +try: + from pybytes_constants import MQTTConstants as mqttConst +except: + from _pybytes_constants import MQTTConstants as mqttConst +try: + from pybytes_debug import print_debug +except: + from _pybytes_debug import print_debug + +import time +import socket +import _thread +import select +import struct + + +class MsgHandler: + + def __init__( + self, + receive_callback, + connect_helper, + receive_timeout=3000, + reconnectMethod=None + ): + self._host = "" + self._port = -1 + self._sock = None + self._output_queue_size = -1 + self._output_queue_dropbehavior = -1 + self._mqttOperationTimeout = 0 + self._connection_state = mqttConst.STATE_DISCONNECTED + self._conn_state_mutex = _thread.allocate_lock() + self._poll = select.poll() + self._output_queue = [] + self._out_packet_mutex = _thread.allocate_lock() + _thread.stack_size(8192) + _thread.start_new_thread(self._io_thread_func, ()) + self._recv_callback = receive_callback + self._connect_helper = connect_helper + self._pingSent = False + self._ping_interval = 20 + self._waiting_ping_resp = False + self._ping_cutoff = 3 + self._receive_timeout = receive_timeout + self.reconnectMethod = reconnectMethod + + def setOfflineQueueConfiguration(self, queueSize, dropBehavior): + self._output_queue_size = queueSize + self._output_queue_dropbehavior = dropBehavior + + def setEndpoint(self, srcHost, srcPort): + self._host = srcHost + self._port = srcPort + + def setOperationTimeout(self, timeout): + self._mqttOperationTimeout = timeout + + def createSocketConnection(self): + self._conn_state_mutex.acquire() + self._connection_state = mqttConst.STATE_CONNECTING + self._conn_state_mutex.release() + try: + if self._sock: + self._poll.unregister(self._sock) + self._sock.close() + self._sock = None + + self._sock = socket.socket() + self._sock.settimeout(30) + self._sock.connect( + socket.getaddrinfo(self._host, self._port)[0][-1] + ) + self._poll.register(self._sock, select.POLLIN) + except socket.error as err: + print_debug(2, "Socket create error: {0}".format(err)) + self._conn_state_mutex.acquire() + self._connection_state = mqttConst.STATE_DISCONNECTED + self._conn_state_mutex.release() + return False + return True + + def disconnect(self): + if self._sock: + self._sock.close() + self._sock = None + + def isConnected(self): + connected = False + self._conn_state_mutex.acquire() + if self._connection_state == mqttConst.STATE_CONNECTED: + connected = True + self._conn_state_mutex.release() + + return connected + + def setConnectionState(self, state): + self._conn_state_mutex.acquire() + self._connection_state = state + self._conn_state_mutex.release() + + def _drop_message(self): + if self._output_queue_size == -1: + return False + elif (self._output_queue_size == 0) and (self._connection_state == mqttConst.STATE_CONNECTED): # noqa + return False + else: + return True if len(self._output_queue) >= self._output_queue_size else False # noqa + + def push_on_send_queue(self, packet): + if self._drop_message(): + if self._output_queue_dropbehavior == mqttConst.DROP_OLDEST: + self._out_packet_mutex.acquire() + if self._out_packet_mutex.locked(): + self._output_queue.pop(0) + self._out_packet_mutex.release() + else: + return False + + self._out_packet_mutex.acquire() + if self._out_packet_mutex.locked(): + self._output_queue.append(packet) + self._out_packet_mutex.release() + + return True + + def priority_send(self, packet): + msg_sent = False + self._out_packet_mutex.acquire() + msg_sent = self._send_packet(packet) + self._out_packet_mutex.release() + + return msg_sent + + def _receive_packet(self): + try: + if not self._poll.poll(self._receive_timeout): + return False + except Exception: + return False + + # Read message type + try: + self._sock.setblocking(False) + msg_type = self._sock.recv(1) + except socket.error: + self.disconnect() + self.reconnectMethod() + return False + else: + if len(msg_type) == 0: + return False + msg_type = struct.unpack("!B", msg_type)[0] + self._sock.setblocking(True) + + # Read payload length + + """ + using the old header checking to read the + payload length of the received packet + """ + + bytes_remaining = 0 + read_bytes = 0 + payload = b'' + sh = 0 + while True: + try: + b = self._sock.read(1)[0] + except Exception: + return False + bytes_remaining |= (b & 0x7f) << sh + if not b & 0x80: + break + sh += 7 + + # Read payload + try: + if self._sock: + read_bytes = bytes_remaining + while read_bytes > 0: + new_payload = self._sock.recv(read_bytes) + payload += new_payload + read_bytes -= len(new_payload) + new_payload = b'' + except socket.error: + return False + return self._recv_callback(msg_type, payload) + + def _send_pingreq(self): + pkt = struct.pack('!BB', mqttConst.MSG_PINGREQ, 0) + return self.priority_send(pkt) + + def setPingFlag(self, flag): + self._pingSent = flag + + def _send_packet(self, packet): + written = -1 + try: + if self._sock: + written = self._sock.write(packet) + if(written is None): + written = -1 + else: + print_debug(2, 'Packet sent. (Length: %d)' % written) + except socket.error as err: + print_debug(2, 'Socket send error {0}'.format(err)) + return False + + return True if len(packet) == written else False + + def _verify_connection_state(self): + elapsed = time.time() - self._start_time + if not self._waiting_ping_resp and elapsed > self._ping_interval: + if self._connection_state == mqttConst.STATE_CONNECTED: + self._pingSent = False + self._send_pingreq() + self._waiting_ping_resp = True + elif self._connection_state == mqttConst.STATE_DISCONNECTED: + self._connect_helper() + + self._start_time = time.time() + elif self._waiting_ping_resp and (self._connection_state == mqttConst.STATE_CONNECTED or elapsed > self._mqttOperationTimeout): # noqa + if not self._pingSent: + if self._ping_failures <= self._ping_cutoff: + self._ping_failures += 1 + else: + self._connect_helper() + else: + self._ping_failures = 0 + + self._start_time = time.time() + self._waiting_ping_resp = False + + def _io_thread_func(self): + self._start_time = time.time() + self._ping_failures = 0 + while True: + self._out_packet_mutex.acquire() + if self._ping_failures == 0: + if self._out_packet_mutex.locked() and len(self._output_queue) > 0: # noqa + packet = self._output_queue[0] + if self._send_packet(packet): + self._output_queue.pop(0) + elif self.reconnectMethod is not None: + self._output_queue.pop(0) + self.disconnect() + self.reconnectMethod() + self._out_packet_mutex.release() + self._receive_packet() diff --git a/esp32/frozen/Pybytes/_pybytes.py b/esp32/frozen/Pybytes/_pybytes.py index e55e26f2d1..2082181865 100644 --- a/esp32/frozen/Pybytes/_pybytes.py +++ b/esp32/frozen/Pybytes/_pybytes.py @@ -1,9 +1,19 @@ +''' +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + import os from machine import Timer + try: from pybytes_connection import PybytesConnection except: from _pybytes_connection import PybytesConnection + try: from pybytes_debug import print_debug except: diff --git a/esp32/frozen/Pybytes/_pybytes_ca.py b/esp32/frozen/Pybytes/_pybytes_ca.py index 58d4c6c782..09d96a6ed0 100644 --- a/esp32/frozen/Pybytes/_pybytes_ca.py +++ b/esp32/frozen/Pybytes/_pybytes_ca.py @@ -1,3 +1,11 @@ +''' +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + PYBYTES_CA = """-----BEGIN CERTIFICATE----- MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT diff --git a/esp32/frozen/Pybytes/_pybytes_config.py b/esp32/frozen/Pybytes/_pybytes_config.py index f329630e4e..92e4e83b4b 100644 --- a/esp32/frozen/Pybytes/_pybytes_config.py +++ b/esp32/frozen/Pybytes/_pybytes_config.py @@ -1,3 +1,11 @@ +''' +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + class PybytesConfig: def read_config(self, file='/flash/pybytes_config.json'): diff --git a/esp32/frozen/Pybytes/_pybytes_connection.py b/esp32/frozen/Pybytes/_pybytes_connection.py index b7c3f49f90..f73d6cf0cd 100644 --- a/esp32/frozen/Pybytes/_pybytes_connection.py +++ b/esp32/frozen/Pybytes/_pybytes_connection.py @@ -1,15 +1,26 @@ +''' +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + try: from mqtt import MQTTClient except: from _mqtt import MQTTClient + try: from pybytes_protocol import PybytesProtocol except: from _pybytes_protocol import PybytesProtocol + try: from pybytes_constants import constants except: from _pybytes_constants import constants + try: from pybytes_debug import print_debug except: @@ -23,6 +34,7 @@ import binascii from machine import WDT + class PybytesConnection: def __init__(self, config, message_callback): self.__conf = config @@ -35,7 +47,9 @@ def __init__(self, config, message_callback): self.__mqtt_upload_topic = "u" + self.__device_id self.__connection = None self.__connection_status = constants.__CONNECTION_STATUS_DISCONNECTED - self.__pybytes_protocol = PybytesProtocol(config, message_callback, pybytes_connection=self) + self.__pybytes_protocol = PybytesProtocol( + config, message_callback, pybytes_connection=self + ) self.__lora_socket = None self.lora = None self.lora_lock = _thread.allocate_lock() @@ -59,25 +73,30 @@ def print_pretty_response(self, rsp): def __initialise_watchdog(self): if self.__conf.get('connection_watchdog', True): - self.__wifi_lte_watchdog = WDT(timeout=constants.__WDT_TIMEOUT_MILLISECONDS) - print('Initialized watchdog for WiFi and LTE connection with timeout {} ms'.format(constants.__WDT_TIMEOUT_MILLISECONDS)) + self.__wifi_lte_watchdog = WDT( + timeout=constants.__WDT_TIMEOUT_MILLISECONDS + ) + print('Initialized watchdog for WiFi and LTE connection with timeout {} ms'.format(constants.__WDT_TIMEOUT_MILLISECONDS)) # noqa else: - print('Watchdog for WiFi and LTE was disabled, enable with "connection_watchdog": true in pybytes_config.json') + print('Watchdog for WiFi and LTE was disabled, enable with "connection_watchdog": true in pybytes_config.json') # noqa # Establish a connection through WIFI before connecting to mqtt server def connect_wifi(self, reconnect=True, check_interval=0.5): self.__initialise_watchdog() - if self.__connection_status != constants.__CONNECTION_STATUS_DISCONNECTED: - print("Error connect_wifi: Connection already exists. Disconnect First") + if self.__connection_status != constants.__CONNECTION_STATUS_DISCONNECTED: # noqa + print("Error connect_wifi: Connection already exists. Disconnect First") # noqa return False try: from network import WLAN antenna = self.__conf.get('wlan_antenna', WLAN.INT_ANT) - known_nets = [((self.__conf['wifi']['ssid'], self.__conf['wifi']['password']))] + known_nets = [((self.__conf['wifi']['ssid'], self.__conf['wifi']['password']))] # noqa if antenna == WLAN.EXT_ANT: print("WARNING! Using external WiFi antenna.") - '''to connect it to an existing network, the WiFi class must be configured as a station''' + + '''to connect it to an existing network, + the WiFi class must be configured as a station''' + self.wlan = WLAN(mode=WLAN.STA, antenna=antenna) available_nets = self.wlan.scan() @@ -87,7 +106,7 @@ def connect_wifi(self, reconnect=True, check_interval=0.5): try: net_to_use = net_to_use[0] pwd = dict(known_nets)[net_to_use] - sec = [e.sec for e in available_nets if e.ssid == net_to_use][0] + sec = [e.sec for e in available_nets if e.ssid == net_to_use][0] # noqa self.wlan.connect(net_to_use, (sec, pwd), timeout=10000) while not self.wlan.isconnected(): time.sleep(0.1) @@ -101,18 +120,25 @@ def connect_wifi(self, reconnect=True, check_interval=0.5): return False self.__network_type = constants.__NETWORK_TYPE_WIFI print("WiFi connection established") - self.__mqtt_check_interval = check_interval try: - self.__connection = MQTTClient(self.__device_id, self.__host, self.__mqtt_download_topic, user=self.__user_name, - password=self.__device_id, reconnect=reconnect, ssl=self.__ssl, ssl_params = self.__ssl_params) + self.__connection = MQTTClient( + self.__device_id, + self.__host, + self.__mqtt_download_topic, + self.__pybytes_protocol, + user=self.__user_name, + password=self.__device_id + ) self.__connection.connect() - self.__connection_status = constants.__CONNECTION_STATUS_CONNECTED_MQTT_WIFI - self.__pybytes_protocol.start_MQTT(self, check_interval, constants.__NETWORK_TYPE_WIFI) - print("Connected to MQTT {}".format(self.__host)) + self.__connection_status = constants.__CONNECTION_STATUS_CONNECTED_MQTT_WIFI # noqa + self.__pybytes_protocol.start_MQTT( + self, + constants.__NETWORK_TYPE_WIFI + ) return True except Exception as ex: if '{}'.format(ex) == '4': - print('MQTT ERROR! Bad credentials when connecting to server: "{}"'.format(self.__host)) + print('MQTT ERROR! Bad credentials when connecting to server: "{}"'.format(self.__host)) # noqa else: print("MQTT ERROR! {}".format(ex)) return False @@ -127,37 +153,54 @@ def connect_lte(self, reconnect=True, check_interval=0.5): lte_cfg = self.__conf.get('lte') if lte_cfg is not None: if (os.uname()[0] not in ['FiPy', 'GPy']): - print("You need a device with FiPy or GPy firmware to connect via LTE") + print("You need a device with FiPy or GPy firmware to connect via LTE") # noqa return False try: from network import LTE time.sleep(3) - print_debug(1, 'LTE init(carrier={}, cid={})'.format(lte_cfg.get('carrier'), lte_cfg.get('cid', 1))) - self.lte = LTE(carrier=lte_cfg.get('carrier')) # instantiate the LTE object - print_debug(1, 'LTE attach(band={}, apn={}, type={})'.format(lte_cfg.get('band'), lte_cfg.get('apn'), lte_cfg.get('type'))) - self.lte.attach(band=lte_cfg.get('band'), apn=lte_cfg.get('apn'), type=lte_cfg.get('type')) # attach the cellular modem to a base station + print_debug(1, 'LTE init(carrier={}, cid={})'.format(lte_cfg.get('carrier'), lte_cfg.get('cid', 1))) # noqa + # instantiate the LTE object + self.lte = LTE(carrier=lte_cfg.get('carrier')) + print_debug( + 1, + 'LTE attach(band={}, apn={}, type={})'.format( + lte_cfg.get('band'), + lte_cfg.get('apn'), + lte_cfg.get('type') + ) + ) + self.lte.attach(band=lte_cfg.get('band'), apn=lte_cfg.get('apn'), type=lte_cfg.get('type')) # noqa # attach the cellular modem to a base station while not self.lte.isattached(): time.sleep(0.25) time.sleep(1) print_debug(1, 'LTE connect()') - self.lte.connect() # start a data session and obtain an IP address + # start a data session and obtain an IP address + self.lte.connect() print_debug(1, 'LTE is_connected()') while not self.lte.isconnected(): time.sleep(0.25) print("LTE connection established") self.__network_type = constants.__NETWORK_TYPE_LTE - self.__mqtt_check_interval = check_interval try: - self.__connection = MQTTClient(self.__device_id, self.__host, self.__mqtt_download_topic, user=self.__user_name, - password=self.__device_id, reconnect=reconnect, ssl=self.__ssl, ssl_params = self.__ssl_params) + self.__connection = MQTTClient( + self.__device_id, + self.__host, + self.__mqtt_download_topic, + self.__pybytes_protocol, + user=self.__user_name, + password=self.__device_id + ) self.__connection.connect() - self.__connection_status = constants.__CONNECTION_STATUS_CONNECTED_MQTT_LTE - self.__pybytes_protocol.start_MQTT(self, check_interval, constants.__NETWORK_TYPE_WIFI) + self.__connection_status = constants.__CONNECTION_STATUS_CONNECTED_MQTT_LTE # noqa + self.__pybytes_protocol.start_MQTT( + self, + constants.__NETWORK_TYPE_WIFI + ) print("Connected to MQTT {}".format(self.__host)) return True except Exception as ex: if '{}'.format(ex) == '4': - print('MQTT ERROR! Bad credentials when connecting to server: "{}"'.format(self.__host)) + print('MQTT ERROR! Bad credentials when connecting to server: "{}"'.format(self.__host)) # noqa else: print("MQTT ERROR! {}".format(ex)) return False @@ -170,8 +213,8 @@ def connect_lte(self, reconnect=True, check_interval=0.5): # LORA def connect_lora_abp(self, lora_timeout, nanogateway): - if (self.__connection_status != constants.__CONNECTION_STATUS_DISCONNECTED): - print("Error connect_lora_abp: Connection already exists. Disconnect First") + if (self.__connection_status != constants.__CONNECTION_STATUS_DISCONNECTED): # noqa + print("Error connect_lora_abp: Connection already exists. Disconnect First") # noqa return False try: from network import LoRa @@ -187,14 +230,17 @@ def connect_lora_abp(self, lora_timeout, nanogateway): app_swkey = self.__conf['lora']['abp']['app_skey'] timeout_ms = self.__conf.get('lora_timeout', lora_timeout) * 1000 - dev_addr = struct.unpack(">l", binascii.unhexlify(dev_addr.replace(' ', '')))[0] + dev_addr = struct.unpack(">l", binascii.unhexlify(dev_addr.replace(' ', '')))[0] # noqa nwk_swkey = binascii.unhexlify(nwk_swkey.replace(' ', '')) app_swkey = binascii.unhexlify(app_swkey.replace(' ', '')) try: print("Trying to join LoRa.ABP for %d seconds..." % lora_timeout) - self.lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey), - timeout=timeout_ms) + self.lora.join( + activation=LoRa.ABP, + auth=(dev_addr, nwk_swkey, app_swkey), + timeout=timeout_ms + ) # if you want, uncomment this code, but timeout must be 0 # while not self.lora.has_joined(): @@ -214,8 +260,8 @@ def connect_lora_abp(self, lora_timeout, nanogateway): return False def connect_lora_otta(self, lora_timeout, nanogateway): - if (self.__connection_status != constants.__CONNECTION_STATUS_DISCONNECTED): - print("Error connect_lora_otta: Connection already exists. Disconnect First") + if (self.__connection_status != constants.__CONNECTION_STATUS_DISCONNECTED): # noqa + print("Error connect_lora_otta: Connection already exists. Disconnect First") # noqa return False try: from network import LoRa @@ -226,7 +272,7 @@ def connect_lora_otta(self, lora_timeout, nanogateway): dev_eui = self.__conf['lora']['otaa']['app_device_eui'] app_eui = self.__conf['lora']['otaa']['app_eui'] app_key = self.__conf['lora']['otaa']['app_key'] - timeout_ms = self.__conf.get('lora_timeout',lora_timeout) * 1000 + timeout_ms = self.__conf.get('lora_timeout', lora_timeout) * 1000 self.lora = LoRa(mode=LoRa.LORAWAN) self.lora.nvram_restore() @@ -236,8 +282,11 @@ def connect_lora_otta(self, lora_timeout, nanogateway): app_key = binascii.unhexlify(app_key.replace(' ', '')) try: print("Trying to join LoRa.OTAA for %d seconds..." % lora_timeout) - self.lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), - timeout=timeout_ms) + self.lora.join( + activation=LoRa.OTAA, + auth=(dev_eui, app_eui, app_key), + timeout=timeout_ms + ) # if you want, uncomment this code, but timeout must be 0 # while not self.lora.has_joined(): @@ -277,12 +326,12 @@ def __open_lora_socket(self, nanogateway): # SIGFOX def connect_sigfox(self): - if (self.__connection_status != constants.__CONNECTION_STATUS_DISCONNECTED): + if (self.__connection_status != constants.__CONNECTION_STATUS_DISCONNECTED): # noqa print("Error: Connection already exists. Disconnect First") pass try: from network import Sigfox - except: + except Exception: print("This device does not support Sigfox connections") return sigfox_config = self.__conf.get('sigfox', {}) @@ -291,14 +340,16 @@ def connect_sigfox(self): try: sf_rcz = int(sigfox_config.get('RCZ', 1)) - 1 if sf_rcz >= 0 and sf_rcz <= 3: - sigfox = Sigfox(mode=Sigfox.SIGFOX, rcz=sf_rcz) - self.__sigfox_socket = socket.socket(socket.AF_SIGFOX, socket.SOCK_RAW) + Sigfox(mode=Sigfox.SIGFOX, rcz=sf_rcz) + self.__sigfox_socket = socket.socket(socket.AF_SIGFOX, socket.SOCK_RAW) # noqa self.__sigfox_socket.setblocking(True) - self.__sigfox_socket.setsockopt(socket.SOL_SIGFOX, socket.SO_RX, False) + self.__sigfox_socket.setsockopt(socket.SOL_SIGFOX, socket.SO_RX, False) # noqa self.__network_type = constants.__NETWORK_TYPE_SIGFOX - self.__connection_status = constants.__CONNECTION_STATUS_CONNECTED_SIGFOX + self.__connection_status = constants.__CONNECTION_STATUS_CONNECTED_SIGFOX # noqa self.__pybytes_protocol.start_Sigfox(self) - print("Connected using Sigfox. Only upload stream is supported") + print( + "Connected using Sigfox. Only upload stream is supported" + ) return True else: print('Invalid Sigfox RCZ specified in config!') @@ -309,29 +360,34 @@ def connect_sigfox(self): # COMMON def disconnect(self): - print_debug(1, 'self.__connection_status={} | self.__network_type={}'.format(self.__connection_status, self.__network_type)) - - if (self.__connection_status == constants.__CONNECTION_STATUS_DISCONNECTED): + print_debug( + 1, + 'self.__connection_status={} | self.__network_type={}'.format( + self.__connection_status, self.__network_type + ) + ) + + if (self.__connection_status == constants.__CONNECTION_STATUS_DISCONNECTED): # noqa print("Already disconnected") pass - if (constants.__CONNECTION_STATUS_CONNECTED_MQTT_WIFI <= self.__connection_status <= constants.__CONNECTION_STATUS_CONNECTED_MQTT_LTE): + if (constants.__CONNECTION_STATUS_CONNECTED_MQTT_WIFI <= self.__connection_status <= constants.__CONNECTION_STATUS_CONNECTED_MQTT_LTE): # noqa print_debug(1, 'MQTT over WIFI||LTE... disconnecting MQTT') try: self.__connection.disconnect() - self.__connection_status = constants.__CONNECTION_STATUS_DISCONNECTED + self.__connection_status = constants.__CONNECTION_STATUS_DISCONNECTED # noqa except Exception as e: print("Error disconnecting: {}".format(e)) - if (self.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_LORA): - print_debug(1, 'Connected over LORA... closing socket and saving nvram') + if (self.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_LORA): # noqa + print_debug(1, 'Connected over LORA... closing socket and saving nvram') # noqa try: self.__lora_socket.close() self.lora.nvram_save() except Exception as e: print("Error disconnecting: {}".format(e)) - if (self.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_SIGFOX): + if (self.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_SIGFOX): # noqa print_debug(1, 'Connected over SIGFOX... closing socket') try: self.__sigfox_socket.close() @@ -349,7 +405,7 @@ def disconnect(self): print_debug(1, 'Connected over LTE... disconnecting') try: lte_cfg = self.__conf.get('lte') - print_debug(1, 'lte.deinit(reset={})'.format(lte_cfg.get('reset', False))) + print_debug(1, 'lte.deinit(reset={})'.format(lte_cfg.get('reset', False))) # noqa self.lte.deinit(reset=lte_cfg.get('reset', False)) except Exception as e: print("Error disconnecting: {}".format(e)) @@ -358,4 +414,4 @@ def disconnect(self): self.__connection_status = constants.__CONNECTION_STATUS_DISCONNECTED def is_connected(self): - return not (self.__connection_status == constants.__CONNECTION_STATUS_DISCONNECTED) + return not (self.__connection_status == constants.__CONNECTION_STATUS_DISCONNECTED) # noqa diff --git a/esp32/frozen/Pybytes/_pybytes_constants.py b/esp32/frozen/Pybytes/_pybytes_constants.py index 9fdbec01f4..aee145e8c4 100644 --- a/esp32/frozen/Pybytes/_pybytes_constants.py +++ b/esp32/frozen/Pybytes/_pybytes_constants.py @@ -1,3 +1,42 @@ +''' +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + +class MQTTConstants: + # - Protocol types + MQTTv3_1 = 3 + MQTTv3_1_1 = 4 + + # - OfflinePublishQueueing drop behavior + DROP_OLDEST = 0 + DROP_NEWEST = 1 + + # Message types + MSG_CONNECT = 0x10 + MSG_CONNACK = 0x20 + MSG_PUBLISH = 0x30 + MSG_PUBACK = 0x40 + MSG_PUBREC = 0x50 + MSG_PUBREL = 0x60 + MSG_PUBCOMP = 0x70 + MSG_SUBSCRIBE = 0x80 + MSG_SUBACK = 0x90 + MSG_UNSUBSCRIBE = 0xA0 + MSG_UNSUBACK = 0xB0 + MSG_PINGREQ = 0xC0 + MSG_PINGRESP = 0xD0 + MSG_DISCONNECT = 0xE0 + + # Connection state + STATE_CONNECTED = 0x01 + STATE_CONNECTING = 0x02 + STATE_DISCONNECTED = 0x03 + + class constants: __NETWORK_INFO_MASK = 0x30 __NETWORK_TYPE_WIFI = 0 @@ -57,7 +96,8 @@ class constants: __DEVICE_TYPE_LOPY_4 = 0x04 __DEVICE_TYPE_UNKNOWN = 0x05 - __WIFI_NETWORK_FORMAT = ">6sBb" # {"ssid":"%s", "mac_addr":"%s", "channel":"%s", "power":"%s"} + # {"ssid":"%s", "mac_addr":"%s", "channel":"%s", "power":"%s"} + __WIFI_NETWORK_FORMAT = ">6sBb" # __USER_SYSTEM_MASK = 0x80 __NETWORK_TYPE_MASK = 0x30 @@ -65,8 +105,8 @@ class constants: __SIGFOX_WARNING = """WARNING! Your sigfox radio configuration (RC) is currently using the default (1) You can set your RC with command (ex: RC 3): pybytes.set_config('sigfox', {'RCZ': 3}) -See all available zone options for RC at https://support.sigfox.com/docs/radio-configuration """ - - __KEEP_ALIVE_PING_INTERVAL = 600 # in seconds - # set watch dog timeout one minute after __KEEP_ALIVE_PING_INTERVAL - __WDT_TIMEOUT_MILLISECONDS = (__KEEP_ALIVE_PING_INTERVAL + 60) * 1000 +See all available zone options for RC at https://support.sigfox.com/docs/radio-configuration """ # noqa + __ONE_MINUTE = 60 # in seconds + __KEEP_ALIVE_PING_INTERVAL = 600 # in seconds + # set watch dog timeout to 21 minutes ~ (2 * 10) + 1 + __WDT_TIMEOUT_MILLISECONDS = (2 * __KEEP_ALIVE_PING_INTERVAL + __ONE_MINUTE) * 1000 # noqa diff --git a/esp32/frozen/Pybytes/_pybytes_debug.py b/esp32/frozen/Pybytes/_pybytes_debug.py index b348ee06ce..7fa973ec10 100644 --- a/esp32/frozen/Pybytes/_pybytes_debug.py +++ b/esp32/frozen/Pybytes/_pybytes_debug.py @@ -1,6 +1,63 @@ -import pycom -DEBUG = pycom.nvs_get('pybytes_debug') +''' +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + +import os +import pycom # pylint: disable=import-error + +from machine import RTC +from time import timezone + +# For compatibility with new 1.18 firmware release +try: + DEBUG = pycom.nvs_get('pybytes_debug') +except: + DEBUG = None + def print_debug(level, msg): + """Print log messages into console.""" if DEBUG is not None and level <= DEBUG: print(msg) + + +def print_debug_local(level, msg): + """ + Print log messages. + + log messages will be stored in the device so + the user can access that using FTP or Flash OTA. + """ + if DEBUG is not None and level <= DEBUG: + print_debug(0, 'adding local log') + rtc = RTC() + if not rtc.synced(): + rtc.ntp_sync("pool.ntp.org") + while not rtc.synced(): + pass + current_year, current_month, current_day, current_hour, current_minute, current_second, current_microsecond, current_tzinfo = rtc.now() # noqa + msg = '\n {}-{}-{} {}:{}:{} (GMT+{}) >>> {}'.format( + current_day, + current_month, + current_year, + current_hour, + current_minute, + current_second, + timezone(), + msg + ) + try: + fsize = os.stat('logs.log') + if fsize.st_size > 1000000: + # logs are bigger than 1 MB + os.remove("logs.log") + except Exception: + pass + + log_file = open('logs.log', 'a+') + log_file.write(msg) + log_file.close() diff --git a/esp32/frozen/Pybytes/_pybytes_library.py b/esp32/frozen/Pybytes/_pybytes_library.py index 89f74b231f..20db346011 100644 --- a/esp32/frozen/Pybytes/_pybytes_library.py +++ b/esp32/frozen/Pybytes/_pybytes_library.py @@ -1,7 +1,16 @@ +''' +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + try: from pybytes_constants import constants except: from _pybytes_constants import constants + try: from pybytes_debug import print_debug except: diff --git a/esp32/frozen/Pybytes/_pybytes_protocol.py b/esp32/frozen/Pybytes/_pybytes_protocol.py index ae709fd0c8..8292243b43 100644 --- a/esp32/frozen/Pybytes/_pybytes_protocol.py +++ b/esp32/frozen/Pybytes/_pybytes_protocol.py @@ -1,23 +1,36 @@ +''' +Copyright (c) 2019, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' + try: from pybytes_library import PybytesLibrary except: from _pybytes_library import PybytesLibrary + try: from pybytes_constants import constants except: from _pybytes_constants import constants + try: from terminal import Terminal except: from _terminal import Terminal + try: from OTA import WiFiOTA except: from _OTA import WiFiOTA + try: from flash_control_OTA import FCOTA except: from _flash_control_OTA import FCOTA + try: from pybytes_debug import print_debug except: @@ -28,16 +41,13 @@ from machine import PWM from machine import Timer from machine import reset -from machine import WDT import os -import sys import _thread import time -import socket import struct import machine -import binascii + class PybytesProtocol: def __init__(self, config, message_callback, pybytes_connection): @@ -46,9 +56,9 @@ def __init__(self, config, message_callback, pybytes_connection): self.__device_id = config['device_id'] self.__mqtt_download_topic = "d" + self.__device_id self.__mqtt_upload_topic = "u" + self.__device_id - self.__mqtt_check_interval = 0.5 self.__pybytes_connection = pybytes_connection - self.__pybytes_library = PybytesLibrary(pybytes_connection=pybytes_connection, pybytes_protocol=self) + self.__pybytes_library = PybytesLibrary( + pybytes_connection=pybytes_connection, pybytes_protocol=self) self.__user_message_callback = message_callback self.__pins = {} self.__pin_modes = {} @@ -66,44 +76,32 @@ def start_Lora(self, pybytes_connection): _thread.stack_size(self.__thread_stack_size) _thread.start_new_thread(self.__check_lora_messages, ()) - def start_MQTT(self, pybytes_connection, check_interval, networkType): - print_debug(5, "This is PybytesProtocol.start_MQTT(check_interval={}, networkType={})".format(check_interval, networkType)) + def start_MQTT(self, pybytes_connection, networkType): + print_debug(5, "This is PybytesProtocol.start_MQTT") self.__pybytes_connection = pybytes_connection self.__pybytes_library.set_network_type(networkType) - self.__mqtt_check_interval = check_interval - self.__start_recv_mqtt() + self.__pybytes_connection.__connection.subscribe( + self.__mqtt_download_topic) + self.__connectionAlarm = Timer.Alarm( + self.__keep_connection, + constants.__KEEP_ALIVE_PING_INTERVAL, + periodic=True + ) def start_Sigfox(self, pybytes_connection): print_debug(5, "This is PybytesProtocol.start_Sigfox()") - self.__pybytes_library.set_network_type(constants.__NETWORK_TYPE_SIGFOX) + self.__pybytes_library.set_network_type( + constants.__NETWORK_TYPE_SIGFOX) self.__pybytes_connection = pybytes_connection - def __start_recv_mqtt(self): - print_debug(5, "This is PybytesProtocol.__start_recv_mqtt()") - self.__pybytes_connection.__connection.set_callback(self.__recv_mqtt) - self.__pybytes_connection.__connection.subscribe(self.__mqtt_download_topic) - print_debug(2, 'Using {} bytes as stack size'.format(self.__thread_stack_size)) - - _thread.stack_size(self.__thread_stack_size) - _thread.start_new_thread(self.__check_mqtt_message, ()) - self.__connectionAlarm = Timer.Alarm(self.__keep_connection, constants.__KEEP_ALIVE_PING_INTERVAL, periodic=True) - def __wifi_or_lte_connection(self): - return self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_MQTT_WIFI or self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_MQTT_LTE - - def __check_mqtt_message(self): - print_debug(5, "This is PybytesProtocol.__check_mqtt_message()") - while self.__wifi_or_lte_connection(): - try: - self.__pybytes_connection.__connection.check_msg() - time.sleep(self.__mqtt_check_interval) - except Exception as ex: - print("Error receiving MQTT. Ignore this message if you disconnected") - print_debug(2, "Exception: {}".format(ex)) - sys.print_exception(ex) + return self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_MQTT_WIFI or self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_MQTT_LTE # noqa def __keep_connection(self, alarm): - print_debug(5, "This is PybytesProtocol.__keep_connection(alarm={})".format(alarm)) + print_debug( + 5, + "This is PybytesProtocol.__keep_connection(alarm={})".format(alarm) + ) if self.__wifi_or_lte_connection(): self.send_ping_message() @@ -116,28 +114,24 @@ def __check_lora_messages(self): self.__pybytes_connection.__lora_socket.setblocking(False) message = self.__pybytes_connection.__lora_socket.recv(256) except Exception as ex: - print_debug(5, "Exception in PybytesProtocol.__check_lora_messages: {}".format(ex)) + print_debug(5, "Exception in PybytesProtocol.__check_lora_messages: {}".format(ex)) # noqa if (message): self.__process_recv_message(message) time.sleep(0.5) - def __recv_mqtt(self, topic, message): - print_debug(5, "This is PybytesProtocol.__recv_mqtt()") - print_debug(2, 'Topic: {}\n Message: {}'.format(topic, message)) - self.__process_recv_message(message) - def __process_recv_message(self, message): print_debug(5, "This is PybytesProtocol.__process_recv_message()") - network_type, message_type, body = self.__pybytes_library.unpack_message(message) - print_debug(2, 'Recv message of type{}'.format(message_type)) - print_debug(6, "network_type={}, message_type={}\nbody={}".format(network_type, message_type, body)) + if message.payload: + network_type, message_type, body = self.__pybytes_library.unpack_message(message.payload) # noqa + else: + # for lora messages + network_type, message_type, body = self.__pybytes_library.unpack_message(message) # noqa if self.__user_message_callback is not None: if (message_type == constants.__TYPE_PING): self.send_ping_message() - elif message_type == constants.__TYPE_PONG and self.__conf.get('connection_watchdog', True): - print_debug(1,'message type pong received, feeding watchdog...') + elif message_type == constants.__TYPE_PONG and self.__conf.get('connection_watchdog', True): # noqa self.__pybytes_connection.__wifi_lte_watchdog.feed() elif (message_type == constants.__TYPE_INFO): @@ -147,20 +141,28 @@ def __process_recv_message(self, message): self.send_network_info_message() elif (message_type == constants.__TYPE_SCAN_INFO): - self.__send_message(self.__pybytes_library.pack_scan_info_message(self.__pybytes_connection.lora)) + self.__send_message( + self.__pybytes_library.pack_scan_info_message( + self.__pybytes_connection.lora + ) + ) elif (message_type == constants.__TYPE_BATTERY_INFO): self.send_battery_info() elif (message_type == constants.__TYPE_OTA): - ota = WiFiOTA(self.__conf['wifi']['ssid'], self.__conf['wifi']['password'], - self.__conf['ota_server']['domain'], self.__conf['ota_server']['port']) - - if (self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_DISCONNECTED): - print('Connecting to WiFi') + ota = WiFiOTA( + self.__conf['wifi']['ssid'], + self.__conf['wifi']['password'], + self.__conf['ota_server']['domain'], + self.__conf['ota_server']['port'] + ) + + if (self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_DISCONNECTED): # noqa + print_debug(5, 'Connecting to WiFi') ota.connect() - print("Performing OTA") + print_debug(5, "Performing OTA") result = ota.update() self.send_ota_response(result) time.sleep(1.5) @@ -170,12 +172,12 @@ def __process_recv_message(self, message): elif (message_type == constants.__TYPE_FCOTA): print_debug(2, 'receiving FCOTA request') - if (self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_DISCONNECTED): - print('Not connected, Re-Connecting ...') + if (self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_DISCONNECTED): # noqa + print_debug(5, 'Not connected, Re-Connecting ...') ota.connect() command = body[0] - if (command == constants.__FCOTA_COMMAND_HIERARCHY_ACQUISITION): + if (command == constants.__FCOTA_COMMAND_HIERARCHY_ACQUISITION): # noqa self.send_fcota_ping('acquiring hierarchy...') hierarchy = self.__FCOTA.get_flash_hierarchy() self.send_fcota_hierarchy(hierarchy) @@ -193,11 +195,11 @@ def __process_recv_message(self, message): splittedBody = bodyString.split(',') if (len(splittedBody) >= 2): path = splittedBody[0] - print(path[len(path)-7:len(path)]) + print_debug(2, path[len(path)-7:len(path)]) if (path[len(path)-7:len(path)] != '.pymakr'): self.send_fcota_ping('updating file...') newContent = bodyString[len(path)+1:len(body)] - if (self.__FCOTA.update_file_content(path, newContent) == True): + if (self.__FCOTA.update_file_content(path, newContent) is True): # noqa size = self.__FCOTA.get_file_size(path) self.send_fcota_file(newContent, path, size) if (path[len(path)-7:len(path)] != '.pymakr'): @@ -219,14 +221,16 @@ def __process_recv_message(self, message): self.send_fcota_ping('deleting file...') path = body[1:len(body)].decode() success = self.__FCOTA.delete_file(path) - if (success == True): + if (success is True): self.send_fcota_ping('file deleted!') - self.send_fcota_hierarchy(self.__FCOTA.get_flash_hierarchy()) + self.send_fcota_hierarchy( + self.__FCOTA.get_flash_hierarchy() + ) else: self.send_fcota_ping('deletion failed!') else: - print("Unknown FCOTA command received") + print_debug(2, "Unknown FCOTA command received") elif (message_type == constants.__TYPE_PYBYTES): command = body[0] @@ -244,12 +248,15 @@ def __process_recv_message(self, message): try: pin_mode = self.__pin_modes[pin_number] except Exception as ex: + print_debug(2, '{}'.format(ex)) pin_mode = Pin.PULL_UP - self.send_pybytes_digital_value(False, pin_number, pin_mode) + self.send_pybytes_digital_value( + False, pin_number, pin_mode + ) elif (command == constants.__COMMAND_DIGITAL_WRITE): - if (not pin_number in self.__pins): + if (pin_number not in self.__pins): self.__configure_digital_pin(pin_number, Pin.OUT, None) pin = self.__pins[pin_number] pin(value) @@ -258,13 +265,13 @@ def __process_recv_message(self, message): self.send_pybytes_analog_value(False, pin_number) elif (command == constants.__COMMAND_ANALOG_WRITE): - if (not pin_number in self.__pins): + if (pin_number not in self.__pins): self.__configure_pwm_pin(pin_number) pin = self.__pins[pin_number] pin.duty_cycle(value * 100) elif (command == constants.__COMMAND_CUSTOM_METHOD): - if (pin_number == constants.__TERMINAL_PIN and self.__terminal_enabled): + if (pin_number == constants.__TERMINAL_PIN and self.__terminal_enabled): # noqa self.__terminal.message_sent_from_pybytes_start() terminal_command = body[2: len(body)] terminal_command = terminal_command.decode("utf-8") @@ -275,7 +282,7 @@ def __process_recv_message(self, message): print(repr(out)) else: print('\n') - except: + except Exception: try: exec(terminal_command) print('\n') @@ -291,13 +298,15 @@ def __process_recv_message(self, message): value = body[i: i + 2] parameters[i / 3] = (value[0] << 8) | value[1] - method_return = self.__custom_methods[pin_number](parameters) + method_return = self.__custom_methods[pin_number](parameters) # noqa - if (method_return is not None and len(method_return) > 0): - self.send_pybytes_custom_method_values(pin_number, method_return) + if (method_return is not None and len(method_return) > 0): # noqa + self.send_pybytes_custom_method_values( + pin_number, method_return + ) else: - print("WARNING: Trying to write to an unregistered Virtual Pin") + print("WARNING: Trying to write to an unregistered Virtual Pin") # noqa else: try: @@ -307,7 +316,11 @@ def __process_recv_message(self, message): def __configure_digital_pin(self, pin_number, pin_mode, pull_mode): # TODO: Add a check for WiPy 1.0 - self.__pins[pin_number] = Pin("P" + str(pin_number), mode = pin_mode, pull=pull_mode) + self.__pins[pin_number] = Pin( + "P" + str(pin_number), + mode=pin_mode, + pull=pull_mode + ) def __configure_analog_pin(self, pin_number): # TODO: Add a check for WiPy 1.0 @@ -332,24 +345,26 @@ def __configure_pwm_pin(self, pin_number): 22: (1, 5), 23: (1, 6)} pwm = PWM(_PWMMap[pin_number][0], frequency=5000) - self.__pins[pin_number] = pwm.channel(_PWMMap[pin_number][1], pin="P" + str(pin_number), - duty_cycle=0) + self.__pins[pin_number] = pwm.channel( + _PWMMap[pin_number][1], pin="P" + str(pin_number), + duty_cycle=0 + ) - def __send_message(self, message, topic=None): + def __send_message(self, message, topic=None, priority=True): try: - finalTopic = self.__mqtt_upload_topic if topic is None else self.__mqtt_upload_topic + "/" + topic - - print_debug(2, "Sending message:[{}] with topic:[{}] and finalTopic: [{}]".format(binascii.hexlify(message), topic, finalTopic)) + finalTopic = self.__mqtt_upload_topic if topic is None else self.__mqtt_upload_topic + "/" + topic # noqa if self.__wifi_or_lte_connection(): - self.__pybytes_connection.__connection.publish(finalTopic, message) - elif (self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_LORA): + self.__pybytes_connection.__connection.publish( + finalTopic, message, priority=priority + ) + elif (self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_LORA): # noqa with self.__pybytes_connection.lora_lock: self.__pybytes_connection.__lora_socket.setblocking(True) self.__pybytes_connection.__lora_socket.send(message) self.__pybytes_connection.__lora_socket.setblocking(False) - elif (self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_SIGFOX): + elif (self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_SIGFOX): # noqa if (len(message) > 12): - print ("WARNING: Message not sent, Sigfox only supports 12 Bytes messages") + print("WARNING: Message not sent, Sigfox only supports 12 Bytes messages") # noqa return self.__pybytes_connection.__sigfox_socket.send(message) @@ -360,7 +375,11 @@ def __send_message(self, message, topic=None): print(ex) def send_user_message(self, message_type, body): - self.__send_message(self.__pybytes_library.pack_user_message(message_type, body)) + self.__send_message( + self.__pybytes_library.pack_user_message( + message_type, body + ) + ) def send_ping_message(self): self.__send_message(self.__pybytes_library.pack_ping_message()) @@ -372,74 +391,117 @@ def send_network_info_message(self): self.__send_message(self.__pybytes_library.pack_network_info_message()) def send_scan_info_message(self, lora): - print('WARNING! send_scan_info_message is deprecated and should be called only from Pybytes.') + print('WARNING! send_scan_info_message is deprecated and should be called only from Pybytes.') # noqa def send_battery_info(self): - self.__send_message(self.__pybytes_library.pack_battery_info(self.__battery_level)) + self.__send_message( + self.__pybytes_library.pack_battery_info( + self.__battery_level + ) + ) def send_ota_response(self, result): print_debug(2, 'Sending OTA result back {}'.format(result)) - self.__send_message(self.__pybytes_library.pack_ota_message(result), 'ota') + self.__send_message( + self.__pybytes_library.pack_ota_message(result), + 'ota' + ) def send_fcota_hierarchy(self, hierarchy): print_debug(2, 'Sending FCOTA hierarchy back') - self.__send_message(self.__pybytes_library.pack_fcota_hierarchy_message(hierarchy), 'fcota') + self.__send_message( + self.__pybytes_library.pack_fcota_hierarchy_message(hierarchy), + 'fcota' + ) def send_fcota_file(self, content, path, size): print_debug(2, 'Sending FCOTA file back') - self.__send_message(self.__pybytes_library.pack_fcota_file_message(content, path, size), 'fcota') + self.__send_message( + self.__pybytes_library.pack_fcota_file_message( + content, path, size + ), + 'fcota' + ) def send_fcota_ping(self, activity): print_debug(2, 'Sending FCOTA ping back: {}'.format(activity)) - self.__send_message(self.__pybytes_library.pack_fcota_ping_message(activity), 'fcota') + self.__send_message( + self.__pybytes_library.pack_fcota_ping_message(activity), + 'fcota' + ) def send_pybytes_digital_value(self, pin_number, pull_mode): - if (not pin_number in self.__pins): + if (pin_number not in self.__pins): self.__configure_digital_pin(pin_number, Pin.IN, pull_mode) pin = self.__pins[pin_number] self.send_pybytes_custom_method_values(pin_number, [pin()]) def send_pybytes_analog_value(self, pin_number): - if (not pin_number in self.__pins): + if (pin_number not in self.__pins): self.__configure_analog_pin(pin_number) pin = self.__pins[pin_number] self.send_pybytes_custom_method_values(pin_number, [pin()]) - def send_pybytes_custom_method_values(self, method_id, parameters): - print(method_id, parameters) if(isinstance(parameters[0], int)): values = bytearray(struct.pack(">i", parameters[0])) values.append(constants.__INTEGER) - self.__send_pybytes_message_variable(constants.__COMMAND_CUSTOM_METHOD, method_id, values) + self.__send_pybytes_message_variable( + constants.__COMMAND_CUSTOM_METHOD, method_id, values + ) elif(isinstance(parameters[0], float)): values = bytearray(struct.pack(" Date: Tue, 20 Aug 2019 18:20:52 +0200 Subject: [PATCH 12/15] updated sqnsupgrade scripts --- esp32/frozen/LTE/sqnsupgrade.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/esp32/frozen/LTE/sqnsupgrade.py b/esp32/frozen/LTE/sqnsupgrade.py index ed4f3cffcf..87d1e72d0a 100644 --- a/esp32/frozen/LTE/sqnsupgrade.py +++ b/esp32/frozen/LTE/sqnsupgrade.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -VERSION = "1.2.5" +VERSION = "1.2.6" # Copyright (c) 2019, Pycom Limited. # @@ -339,7 +339,7 @@ def __get_wait_msg(self, load_fff=True): - def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_ffh=False, mirror=False, switch_ffh=False, bootrom=False, rgbled=0x050505, debug=False, pkgdebug=False, atneg=True, max_try=10, direct=True, atneg_only=False, info_only=False, expected_smod=None, verbose=False, load_fff=False, mtools=False): + def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_ffh=False, mirror=False, switch_ffh=False, bootrom=False, rgbled=0x050505, debug=False, pkgdebug=False, atneg=True, max_try=10, direct=True, atneg_only=False, info_only=False, expected_smod=None, verbose=False, load_fff=False, mtools=False, fc=False): self.__wait_msg = False mirror = True if atneg_only else mirror recover = True if atneg_only else load_ffh @@ -365,7 +365,7 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f external = True br = 115200 if recover and not direct else baudrate if debug: print('Setting baudrate to {}'.format(br)) - self.__serial = serial.Serial(port, br, bytesize=serial.EIGHTBITS, timeout=1 if info_only else 0.1) + self.__serial = serial.Serial(port, br, bytesize=serial.EIGHTBITS, timeout=1 if info_only else 0.1, rtscts=fc) self.__serial.reset_input_buffer() self.__serial.reset_output_buffer() @@ -447,7 +447,7 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f if verbose: print('Sending AT+FSRDFILE="/fs/crashdump"') self.__serial.write(b'AT+FSRDFILE="/fs/crashdump"\r\n') - response = self.read_rsp(size=100) + response = self.read_rsp(size=1024) if verbose: print('AT+FSRDFILE="/fs/crashdump" returned {}'.format(response)) self.__serial.read() @@ -819,7 +819,7 @@ def at_negotiation(self, baudrate, port, max_try, mirror, atneg_only, debug, tar self.__serial = UART(1, baudrate=target_baudrate, pins=self.__pins, timeout_chars=100) else: self.__serial = None - self.__serial = serial.Serial(port, target_baudrate, bytesize=serial.EIGHTBITS, timeout=0.1) + self.__serial = serial.Serial(port, target_baudrate, bytesize=serial.EIGHTBITS, timeout=0.1, rtscts=fc) self.__serial.reset_input_buffer() self.__serial.reset_output_buffer() self.__serial.flush() @@ -882,10 +882,11 @@ def upgrade(self, ffile, mfile=None, baudrate=921600, retry=False, resume=False, if success: if self.__run(file_path=ffile, resume=True if mfile is not None else resume, baudrate=baudrate, direct=False, debug=debug, pkgdebug=pkgdebug, verbose=verbose, load_fff=False if mfile else load_fff, mtools=mtools): if self.__check_br(verbose=verbose, debug=debug): - self.__run(bootrom=True, debug=debug, direct=False, pkgdebug=pkgdebug, verbose=verbose, load_fff=True) + success = self.__run(bootrom=True, debug=debug, direct=False, pkgdebug=pkgdebug, verbose=verbose, load_fff=True) self.success_message(verbose=verbose, debug=debug) else: print('Unable to load updater from {}'.format(mfile)) + return success def upgrade_uart(self, ffh_mode=False, mfile=None, retry=False, resume=False, color=0x050505, debug=False, pkgdebug=False, verbose=False, load_fff=True): success = False @@ -923,16 +924,16 @@ def upgrade_uart(self, ffh_mode=False, mfile=None, retry=False, resume=False, co else: print('Unable to upgrade bootrom.') - def show_info(self, port=None, debug=False, verbose=False): - self.__run(port=port, debug=debug, info_only=True, verbose=verbose) + def show_info(self, port=None, debug=False, verbose=False, fc=False): + self.__run(port=port, debug=debug, info_only=True, verbose=verbose, fc=fc) - def upgrade_ext(self, port, ffile, mfile, resume=False, debug=False, pkgdebug=False, verbose=False, load_fff=True): + def upgrade_ext(self, port, ffile, mfile, resume=False, debug=False, pkgdebug=False, verbose=False, load_fff=True, fc=False): success = True if mfile is not None: success = False - success = self.__run(file_path=mfile, load_ffh=True, port=port, debug=debug, pkgdebug=pkgdebug, verbose=verbose) + success = self.__run(file_path=mfile, load_ffh=True, port=port, debug=debug, pkgdebug=pkgdebug, verbose=verbose, fc=fc) if success: - if self.__run(file_path=ffile, resume=True if mfile is not None else resume, direct=False, port=port, debug=debug, pkgdebug=pkgdebug, verbose=verbose, load_fff=load_fff): + if self.__run(file_path=ffile, resume=True if mfile is not None else resume, direct=False, port=port, debug=debug, pkgdebug=pkgdebug, verbose=verbose, load_fff=load_fff, fc=fc): self.success_message(port=port, verbose=verbose, debug=debug) else: print('Unable to load updater from {}'.format(mfile)) @@ -973,6 +974,7 @@ def run(ffile, mfile=None, baudrate=921600, verbose=False, debug=False, load_fff retry = False resume = False mtools = False + success = False sqnup = sqnsupgrade() if sqnup.check_files(ffile, mfile, debug): state = sqnup.detect_modem_state(debug=debug, hangup=hangup) @@ -991,8 +993,9 @@ def run(ffile, mfile=None, baudrate=921600, verbose=False, debug=False, load_fff mtools = True elif state == -1: detect_error() - sqnup.upgrade(ffile=ffile, mfile=mfile, baudrate=baudrate, retry=retry, resume=resume, debug=debug, pkgdebug=False, verbose=verbose, load_fff=load_fff, mtools=mtools) + success = sqnup.upgrade(ffile=ffile, mfile=mfile, baudrate=baudrate, retry=retry, resume=resume, debug=debug, pkgdebug=False, verbose=verbose, load_fff=load_fff, mtools=mtools) reconnect_uart() + return success def uart(ffh_mode=False, mfile=None, color=0x050505, verbose=False, debug=False, hangup=True): print_welcome() @@ -1054,12 +1057,12 @@ def state(verbose=False, debug=False, retry=5, hangup=False): return sqnup.detect_modem_state(debug=debug, hangup=hangup, retry=retry) else: - def run(port, ffile, mfile=None, resume=False, debug=False, verbose=False, load_fff=True): + def run(port, ffile, mfile=None, resume=False, debug=False, verbose=False, load_fff=True, fc=False): print_welcome() sqnup = sqnsupgrade() if sqnup.check_files(ffile, mfile, debug): sqnup.upgrade_ext(port=port, ffile=ffile, mfile=mfile, resume=resume, debug=debug, pkgdebug=False, verbose=verbose, load_fff=load_fff) - def version(port, verbose=False, debug=False): + def version(port, verbose=False, debug=False, fc=False): sqnup = sqnsupgrade() sqnup.show_info(port=port, debug=debug, verbose=verbose) From 80fbb2b08dec9113542f8c83bbc1d65874ae2b27 Mon Sep 17 00:00:00 2001 From: iwahdan88 Date: Wed, 21 Aug 2019 11:36:10 +0200 Subject: [PATCH 13/15] Updated Makefiles --- esp32/Makefile | 5 ++--- esp32/application.mk | 24 +++++++++++++++--------- esp32/tools/idfVerCheck.sh | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 esp32/tools/idfVerCheck.sh diff --git a/esp32/Makefile b/esp32/Makefile index 0468ea9da6..d15d8fef76 100644 --- a/esp32/Makefile +++ b/esp32/Makefile @@ -14,6 +14,8 @@ ifeq ($(wildcard boards/$(BOARD)/.),) $(error Invalid BOARD specified) endif +IDF_VERSION=3.1 + TARGET ?= boot_app OEM ?= 0 @@ -70,9 +72,6 @@ ifndef PROJECT_PATH PROJECT_PATH := $(abspath $(dir $(firstword $(MAKEFILE_LIST)))) endif -#check Frozen files changes -$(shell bash tools/mpy-build-check.sh $(BOARD) $(BTYPE) $(VARIANT)) - FROZEN_MPY_DIR = frozen include ../py/mkenv.mk diff --git a/esp32/application.mk b/esp32/application.mk index 0adb380ac0..cd37579e19 100644 --- a/esp32/application.mk +++ b/esp32/application.mk @@ -503,7 +503,7 @@ endif ifeq ($(TARGET), boot_app) all: $(BOOT_BIN) $(APP_BIN) endif -.PHONY: all +.PHONY: all CHECK_DEP $(info $(VARIANT) Variant) ifeq ($(SECURE), on) @@ -562,10 +562,6 @@ $(BUILD)/bootloader/bootloader.a: $(BOOT_OBJ) sdkconfig.h $(Q) $(AR) cru $@ $^ $(BUILD)/bootloader/bootloader.elf: $(BUILD)/bootloader/bootloader.a $(SECURE_BOOT_VERIFICATION_KEY) -ifeq ($(COPY_IDF_LIB), 1) - $(ECHO) "COPY IDF LIBRARIES $@" - $(Q) $(PYTHON) get_idf_libs.py --idflibs $(IDF_PATH)/examples/wifi/scan/build -endif ifeq ($(SECURE), on) # unpack libbootloader_support.a, and archive again using the right key for verifying signatures $(ECHO) "Inserting verification key $(SECURE_BOOT_VERIFICATION_KEY) in $@" @@ -627,10 +623,6 @@ $(BUILD)/application.a: $(OBJ) $(Q) rm -f $@ $(Q) $(AR) cru $@ $^ $(BUILD)/application.elf: $(BUILD)/application.a $(BUILD)/esp32_out.ld $(SECURE_BOOT_VERIFICATION_KEY) -ifeq ($(COPY_IDF_LIB), 1) - $(ECHO) "COPY IDF LIBRARIES $@" - $(Q) $(PYTHON) get_idf_libs.py --idflibs $(IDF_PATH)/examples/wifi/scan/build -endif ifeq ($(SECURE), on) # unpack libbootloader_support.a, and archive again using the right key for verifying signatures $(ECHO) "Inserting verification key $(SECURE_BOOT_VERIFICATION_KEY) in $@" @@ -750,6 +742,11 @@ GEN_PINS_SRC = $(BUILD)/pins.c GEN_PINS_HDR = $(HEADER_BUILD)/pins.h GEN_PINS_QSTR = $(BUILD)/pins_qstr.h +.NOTPARALLEL: CHECK_DEP $(OBJ) +.NOTPARALLEL: CHECK_DEP $(BOOT_OBJ) + +$(BOOT_OBJ) $(OBJ): | CHECK_DEP + # Making OBJ use an order-only dependency on the generated pins.h file # has the side effect of making the pins.h file before we actually compile # any of the objects. The normal dependency generation will deal with the @@ -757,6 +754,15 @@ GEN_PINS_QSTR = $(BUILD)/pins_qstr.h # which source files might need it. $(OBJ): | $(GEN_PINS_HDR) +# Check Dependencies (IDF version, Frozen code and IDF LIBS) +CHECK_DEP: + $(Q) bash tools/idfVerCheck.sh $(IDF_PATH) "$(IDF_VERSION)" + $(Q) bash tools/mpy-build-check.sh $(BOARD) $(BTYPE) $(VARIANT) +ifeq ($(COPY_IDF_LIB), 1) + $(ECHO) "COPY IDF LIBRARIES" + $(Q) $(PYTHON) get_idf_libs.py --idflibs $(IDF_PATH)/examples/wifi/scan/build +endif + # Call make-pins.py to generate both pins_gen.c and pins.h $(GEN_PINS_SRC) $(GEN_PINS_HDR) $(GEN_PINS_QSTR): $(BOARD_PINS) $(MAKE_PINS) $(AF_FILE) $(PREFIX_FILE) | $(HEADER_BUILD) $(ECHO) "Create $@" diff --git a/esp32/tools/idfVerCheck.sh b/esp32/tools/idfVerCheck.sh new file mode 100644 index 0000000000..1ac3e3231b --- /dev/null +++ b/esp32/tools/idfVerCheck.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# +# Copyright (c) 2019, Pycom Limited. +# +# This software is licensed under the GNU GPL version 3 or any +# later version, with permitted additional terms. For more information +# see the Pycom Licence v1.0 document supplied with this file, or +# available at https://www.pycom.io/opensource/licensing +# + +IDF_VER="idf_v"$2 +CURR_VER="$(git --git-dir=$1/.git branch | grep \* | cut -d ' ' -f2)" + +if [ "${CURR_VER}" = "${IDF_VER}" ]; then + echo "IDF Version OK!" + exit 0 +else + echo "Incompatible IDF version...Checking out IDF version $2!" + if ! git --git-dir=$1/.git --work-tree=$1 checkout ${IDF_VER} ; then + echo "Cannot checkout IDF version ${IDF_VER}!...Please make sure latest idf_v${IDF_VER} branch is fetched" >&2 + exit 1 + fi + cd ${IDF_PATH} + if ! git submodule sync ; then + echo "Cannot checkout IDF version ${IDF_VER}!...Please make sure latest idf_v${IDF_VER} branch is fetched" >&2 + exit 1 + fi + if ! git submodule update --init ; then + echo "Cannot checkout IDF version ${IDF_VER}!...Please make sure latest idf_v${IDF_VER} branch is fetched" >&2 + exit 1 + fi + exit 0 +fi \ No newline at end of file From 642aa6a7e773d502406f4463d05e8c4519fa75cb Mon Sep 17 00:00:00 2001 From: iwahdan88 Date: Wed, 21 Aug 2019 11:36:24 +0200 Subject: [PATCH 14/15] Updated Version --- esp32/pycom_version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esp32/pycom_version.h b/esp32/pycom_version.h index c13efbe5fa..7444eef19d 100644 --- a/esp32/pycom_version.h +++ b/esp32/pycom_version.h @@ -10,14 +10,14 @@ #ifndef VERSION_H_ #define VERSION_H_ -#define SW_VERSION_NUMBER "1.20.0.rc12.1" +#define SW_VERSION_NUMBER "1.20.0.rc13" #define LORAWAN_VERSION_NUMBER "1.0.2" #define SIGFOX_VERSION_NUMBER "1.0.1" #if (VARIANT == PYBYTES) -#define PYBYTES_VERSION_NUMBER "0.9.12" +#define PYBYTES_VERSION_NUMBER "1.0.0" #endif #endif /* VERSION_H_ */ From 81ade144fe3bf03dfc45ce5da8f992d765ecbfb5 Mon Sep 17 00:00:00 2001 From: oligauc Date: Wed, 21 Aug 2019 19:33:52 +0200 Subject: [PATCH 15/15] Lorawan: Descriptive errors, refactoring of region, etc (#52) --- esp32/mods/modlora.c | 2 +- lib/lora/mac/LoRaMac.c | 242 +++++++++++------------ lib/lora/mac/LoRaMac.h | 61 +----- lib/lora/mac/region/Region.h | 11 +- lib/lora/mac/region/RegionAS923.c | 8 +- lib/lora/mac/region/RegionAU915.c | 11 +- lib/lora/mac/region/RegionCN470.c | 8 +- lib/lora/mac/region/RegionEU868.c | 14 +- lib/lora/mac/region/RegionIN865.c | 79 +++----- lib/lora/mac/region/RegionKR920.c | 8 +- lib/lora/mac/region/RegionUS915-Hybrid.c | 16 +- lib/lora/mac/region/RegionUS915.c | 2 +- 12 files changed, 160 insertions(+), 302 deletions(-) diff --git a/esp32/mods/modlora.c b/esp32/mods/modlora.c index 617332e665..d99a67fe49 100644 --- a/esp32/mods/modlora.c +++ b/esp32/mods/modlora.c @@ -1091,7 +1091,7 @@ static void TASK_LoRa (void *pvParameters) { mlmeReq.Req.Join.AppEui = (uint8_t *)lora_obj.u.otaa.AppEui; mlmeReq.Req.Join.AppKey = (uint8_t *)lora_obj.u.otaa.AppKey; mlmeReq.Req.Join.NbTrials = 1; - mlmeReq.Req.Join.Datarate = (uint8_t) lora_obj.otaa_dr; + mlmeReq.Req.Join.DR = (uint8_t) lora_obj.otaa_dr; LoRaMacMlmeRequest( &mlmeReq ); } else { mibReq.Type = MIB_NETWORK_ACTIVATION; diff --git a/lib/lora/mac/LoRaMac.c b/lib/lora/mac/LoRaMac.c index 789d74a98b..aaf8027127 100644 --- a/lib/lora/mac/LoRaMac.c +++ b/lib/lora/mac/LoRaMac.c @@ -539,24 +539,6 @@ static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ); */ static uint8_t ParseMacCommandsToRepeat( uint8_t* cmdBufIn, uint8_t length, uint8_t* cmdBufOut ); -/*! - * \brief Validates if the payload fits into the frame, taking the datarate - * into account. - * - * \details Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0 - * - * \param lenN Length of the application payload. The length depends on the - * datarate and is region specific - * - * \param datarate Current datarate - * - * \param fOptsLen Length of the fOpts field - * - * \retval [false: payload does not fit into the frame, true: payload fits into - * the frame] - */ -bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ); - /*! * \brief Decodes MAC commands in the fOpts field and in the payload */ @@ -589,11 +571,9 @@ LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl /* * \brief Schedules the frame according to the duty cycle * - * \param [IN] allowDelayedTx When set to true, the a frame will be delayed, - * the duty cycle restriction is active * \retval Status of the operation */ -static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ); +static LoRaMacStatus_t ScheduleTx( void ); /* * \brief Calculates the back-off time for the band of a channel. @@ -1180,13 +1160,9 @@ static void OnRadioRxError( void ) } MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR; - if( LoRaMacDeviceClass != CLASS_C ) + if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay + 50) { - if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay ) - { - TimerStop( &RxWindowTimer2 ); - LoRaMacFlags.Bits.MacDone = 1; - } + LoRaMacFlags.Bits.MacDone = 1; } } else @@ -1196,11 +1172,7 @@ static void OnRadioRxError( void ) McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; } MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; - - if( LoRaMacDeviceClass != CLASS_C ) - { - LoRaMacFlags.Bits.MacDone = 1; - } + LoRaMacFlags.Bits.MacDone = 1; } if( LoRaMacDeviceClass == CLASS_C ) @@ -1215,7 +1187,11 @@ static void OnRadioRxTimeout( void ) { Radio.Sleep( ); } - + else + { + OnRxWindow2TimerEvent( ); + } + if( RxSlot == 0 ) { if( NodeAckRequested == true ) @@ -1224,13 +1200,9 @@ static void OnRadioRxTimeout( void ) } MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT; - if( LoRaMacDeviceClass != CLASS_C ) + if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay + 50) { - if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay ) - { - TimerStop( &RxWindowTimer2 ); - LoRaMacFlags.Bits.MacDone = 1; - } + LoRaMacFlags.Bits.MacDone = 1; } } else @@ -1296,9 +1268,23 @@ static void OnMacStateCheckTimerEvent( void ) {// Node joined successfully UpLinkCounter = 0; ChannelsNbRepCounter = 0; - //LoRaMacState &= ~LORAMAC_TX_RUNNING; + LoRaMacState &= ~LORAMAC_TX_RUNNING; + } + else + { + // Pycom: Do not resend join requests + + // if( JoinRequestTrials >= MaxJoinRequestTrials ) + // { + LoRaMacState &= ~LORAMAC_TX_RUNNING; + // } + // else + // { + // LoRaMacFlags.Bits.MacDone = 0; + // // Sends the same frame again + // OnTxDelayedTimerEvent( ); + // } } - LoRaMacState &= ~LORAMAC_TX_RUNNING; } else {// Procedure for all other frames @@ -1361,8 +1347,8 @@ static void OnMacStateCheckTimerEvent( void ) phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); LoRaMacParams.ChannelsDatarate = phyParam.Value; } - // Try to send the frame again. Allow delayed frame transmissions - if( ScheduleTx( true ) == LORAMAC_STATUS_OK ) + // Try to send the frame again + if( ScheduleTx( ) == LORAMAC_STATUS_OK ) { LoRaMacFlags.Bits.MacDone = 0; } @@ -1406,18 +1392,32 @@ static void OnMacStateCheckTimerEvent( void ) LoRaMacState &= ~LORAMAC_RX; } + if( LoRaMacFlags.Bits.McpsInd == 1 ) + { + if( LoRaMacDeviceClass == CLASS_C ) + {// Activate RX2 window for Class C + OnRxWindow2TimerEvent( ); + } + if( LoRaMacFlags.Bits.McpsIndSkip == 0 ) + { + LoRaMacPrimitives->MacMcpsIndication( &McpsIndication ); + } + LoRaMacFlags.Bits.McpsIndSkip = 0; + LoRaMacFlags.Bits.McpsInd = 0; + } + if( LoRaMacState == LORAMAC_IDLE ) { if( LoRaMacFlags.Bits.McpsReq == 1 ) { - LoRaMacFlags.Bits.McpsReq = 0; LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm ); + LoRaMacFlags.Bits.McpsReq = 0; } if( LoRaMacFlags.Bits.MlmeReq == 1 ) { - LoRaMacFlags.Bits.MlmeReq = 0; LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm ); + LoRaMacFlags.Bits.MlmeReq = 0; } // Verify if sticky MAC commands are pending or not @@ -1461,11 +1461,33 @@ static void OnMacStateCheckTimerEvent( void ) static void OnTxDelayedTimerEvent( void ) { + LoRaMacHeader_t macHdr; + LoRaMacFrameCtrl_t fCtrl; + AlternateDrParams_t altDr; + TimerStop( &TxDelayedTimer ); LoRaMacState &= ~LORAMAC_TX_DELAYED; - // Schedule frame, allow delayed frame transmissions - ScheduleTx( true ); + if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) ) + { + ResetMacParameters( ); + + altDr.NbTrials = JoinRequestTrials + 1; + LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, &altDr ); + + macHdr.Value = 0; + macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; + + fCtrl.Value = 0; + fCtrl.Bits.Adr = AdrCtrlOn; + + /* In case of join request retransmissions, the stack must prepare + * the frame again, because the network server keeps track of the random + * LoRaMacDevNonce values to prevent reply attacks. */ + PrepareFrame( &macHdr, &fCtrl, 0, NULL, 0 ); + } + + ScheduleTx( ); } static void OnRxWindow1TimerEvent( void ) @@ -1478,7 +1500,7 @@ static void OnRxWindow1TimerEvent( void ) RxWindow1Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; RxWindow1Config.RepeaterSupport = RepeaterSupport; RxWindow1Config.RxContinuous = false; - RxWindow1Config.RxSlot = RxSlot; + RxWindow1Config.Window = RxSlot; if( LoRaMacDeviceClass == CLASS_C ) { @@ -1497,7 +1519,7 @@ static void OnRxWindow2TimerEvent( void ) RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency; RxWindow2Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; RxWindow2Config.RepeaterSupport = RepeaterSupport; - RxWindow2Config.RxSlot = 1; + RxWindow2Config.Window = 1; if( LoRaMacDeviceClass != CLASS_C ) { @@ -1505,17 +1527,16 @@ static void OnRxWindow2TimerEvent( void ) } else { - // Setup continuous listening for class c RxWindow2Config.RxContinuous = true; - /*if (NetworkActivation == ACTIVATION_TYPE_ABP){ + if (NetworkActivation == ACTIVATION_TYPE_ABP){ RxWindow2Config.Datarate = McpsIndication.RxDatarate; - }*/ + } } if( RegionRxConfig( LoRaMacRegion, &RxWindow2Config, ( int8_t* )&McpsIndication.RxDatarate ) == true ) { RxWindowSetup( RxWindow2Config.RxContinuous, LoRaMacParams.MaxRxWindow ); - RxSlot = 1; + RxSlot = RxWindow2Config.Window; } } @@ -1875,7 +1896,7 @@ static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t comm { batteryLevel = LoRaMacCallbacks->GetBatteryLevel( ); } - AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, batteryLevel, snr & 0x3F); + AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, batteryLevel, snr ); break; } case SRV_MAC_NEW_CHANNEL_REQ: @@ -1991,15 +2012,13 @@ LoRaMacStatus_t Send( LoRaMacHeader_t *macHdr, uint8_t fPort, void *fBuffer, uin McpsConfirm.AckReceived = false; McpsConfirm.UpLinkCounter = UpLinkCounter; - // Schedule frame, do not allow delayed transmissions - status = ScheduleTx( false ); + status = ScheduleTx( ); return status; } -static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ) +static LoRaMacStatus_t ScheduleTx( void ) { - LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; TimerTime_t dutyCycleTimeOff = 0; NextChanParams_t nextChan; @@ -2023,27 +2042,12 @@ static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ) nextChan.LastAggrTx = AggregatedLastTxDoneTime; // Select channel - status = RegionNextChannel( LoRaMacRegion, &nextChan, &Channel, &dutyCycleTimeOff, &AggregatedTimeOff ); - - if( status != LORAMAC_STATUS_OK ) + while( RegionNextChannel( LoRaMacRegion, &nextChan, &Channel, &dutyCycleTimeOff, &AggregatedTimeOff ) == false ) { - if( ( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) && - ( allowDelayedTx == true ) ) - { - // Allow delayed transmissions. We have to allow it in case - // the MAC must retransmit a frame with the frame repetitions - if( dutyCycleTimeOff != 0 ) - {// Send later - prepare timer - LoRaMacState |= LORAMAC_TX_DELAYED; - TimerSetValue( &TxDelayedTimer, dutyCycleTimeOff ); - TimerStart( &TxDelayedTimer ); - } - return LORAMAC_STATUS_OK; - } - else - {// State where the MAC cannot send a frame - return status; - } + // Set the default datarate + LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate; + // Update datarate in the function parameters + nextChan.Datarate = LoRaMacParams.ChannelsDatarate; } // Compute Rx1 windows parameters @@ -2061,8 +2065,8 @@ static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ) if( IsLoRaMacNetworkJoined == false ) { - RxWindow1Delay = LoRaMacParams.JoinAcceptDelay1 + RxWindow1Config.WindowOffset; - RxWindow2Delay = LoRaMacParams.JoinAcceptDelay2 + RxWindow2Config.WindowOffset; + RxWindow1Delay = LoRaMacParams.JoinAcceptDelay1 + RxWindow1Config.WindowOffset - 2; + RxWindow2Delay = LoRaMacParams.JoinAcceptDelay2 + RxWindow2Config.WindowOffset - 3; } else { @@ -2070,14 +2074,11 @@ static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ) { return LORAMAC_STATUS_LENGTH_ERROR; } - RxWindow1Delay = LoRaMacParams.ReceiveDelay1 + RxWindow1Config.WindowOffset; - RxWindow2Delay = LoRaMacParams.ReceiveDelay2 + RxWindow2Config.WindowOffset; + RxWindow1Delay = LoRaMacParams.ReceiveDelay1 + RxWindow1Config.WindowOffset - 1; + RxWindow2Delay = LoRaMacParams.ReceiveDelay2 + RxWindow2Config.WindowOffset - 2; } - // Try to send now - return SendFrameOnChannel( Channel ); - - /* // Schedule transmission of frame + // Schedule transmission of frame if( dutyCycleTimeOff == 0 ) { // Try to send now @@ -2091,7 +2092,7 @@ static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ) TimerStart( &TxDelayedTimer ); return LORAMAC_STATUS_OK; - } */ + } } static void CalculateBackOff( uint8_t channel ) @@ -2108,9 +2109,8 @@ static void CalculateBackOff( uint8_t channel ) // Update regional back-off RegionCalcBackOff( LoRaMacRegion, &calcBackOff ); - // Update aggregated time-off. This must be an assignment and no incremental - // update as we do only calculate the time-off based on the last transmission - AggregatedTimeOff = ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir ); + // Update aggregated time-off + AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir ); } static void ResetMacParameters( void ) @@ -2145,9 +2145,6 @@ static void ResetMacParameters( void ) LoRaMacParams.MaxEirp = LoRaMacParamsDefaults.MaxEirp; LoRaMacParams.AntennaGain = LoRaMacParamsDefaults.AntennaGain; - // Reset to application defaults - RegionInitDefaults( LoRaMacRegion, INIT_TYPE_APP_DEFAULTS ); - NodeAckRequested = false; SrvAckRequested = false; MacCommandsInNextTx = false; @@ -2364,7 +2361,6 @@ LoRaMacStatus_t SendFrameOnChannel( uint8_t channel ) McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; McpsConfirm.Datarate = LoRaMacParams.ChannelsDatarate; McpsConfirm.TxPower = txPower; - McpsConfirm.Channel = channel; McpsConfirm.UpLinkFrequency = Radio.GetChannel(); // Store the time on air @@ -2814,11 +2810,6 @@ LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet ) mibGet->Param.AntennaGain = LoRaMacParams.AntennaGain; break; } - case MIB_DEFAULT_ANTENNA_GAIN: - { - mibGet->Param.DefaultAntennaGain = LoRaMacParamsDefaults.AntennaGain; - break; - } default: status = LORAMAC_STATUS_SERVICE_UNKNOWN; break; @@ -2863,15 +2854,7 @@ LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ) { // Set the NodeAckRequested indicator to default NodeAckRequested = false; - // Set the radio into sleep mode in case we are still in RX mode - Radio.Sleep( ); - // Compute Rx2 windows parameters in case the RX2 datarate has changed - RegionComputeRxWindowParameters( LoRaMacRegion, - LoRaMacParams.Rx2Channel.Datarate, - LoRaMacParams.MinRxSymbols, - LoRaMacParams.SystemMaxRxError, - &RxWindow2Config ); - OpenContinuousRx2Window( ); + OnRxWindow2TimerEvent( ); break; } } @@ -2957,18 +2940,29 @@ LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ) if( ( LoRaMacDeviceClass == CLASS_C ) && ( IsLoRaMacNetworkJoined == true ) ) { - // We can only compute the RX window parameters directly, if we are already - // in class c mode and joined. We cannot setup an RX window in case of any other - // class type. - // Set the radio into sleep mode in case we are still in RX mode - Radio.Sleep( ); // Compute Rx2 windows parameters RegionComputeRxWindowParameters( LoRaMacRegion, LoRaMacParams.Rx2Channel.Datarate, LoRaMacParams.MinRxSymbols, LoRaMacParams.SystemMaxRxError, &RxWindow2Config ); - OpenContinuousRx2Window( ); + + RxWindow2Config.Channel = Channel; + RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency; + RxWindow2Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; + RxWindow2Config.RepeaterSupport = RepeaterSupport; + RxWindow2Config.Window = 1; + RxWindow2Config.RxContinuous = true; + + if( RegionRxConfig( LoRaMacRegion, &RxWindow2Config, ( int8_t* )&McpsIndication.RxDatarate ) == true ) + { + RxWindowSetup( RxWindow2Config.RxContinuous, LoRaMacParams.MaxRxWindow ); + RxSlot = RxWindow2Config.Window; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } } } else @@ -3069,7 +3063,6 @@ LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ) case MIB_CHANNELS_DATARATE: { verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate; - verify.DatarateParams.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; if( RegionVerify( LoRaMacRegion, &verify, PHY_TX_DR ) == true ) { @@ -3134,11 +3127,6 @@ LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ) LoRaMacParams.AntennaGain = mibSet->Param.AntennaGain; break; } - case MIB_DEFAULT_ANTENNA_GAIN: - { - LoRaMacParamsDefaults.AntennaGain = mibSet->Param.DefaultAntennaGain; - break; - } default: status = LORAMAC_STATUS_SERVICE_UNKNOWN; break; @@ -3329,7 +3317,7 @@ LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ) { return LORAMAC_STATUS_PARAMETER_INVALID; } - if( LoRaMacState != LORAMAC_IDLE ) + if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) { return LORAMAC_STATUS_BUSY; } @@ -3355,8 +3343,6 @@ LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ) return LORAMAC_STATUS_PARAMETER_INVALID; } - ResetMacParameters( ); - // Verify the parameter NbTrials for the join procedure verify.NbJoinTrials = mlmeRequest->Req.Join.NbTrials; @@ -3374,7 +3360,6 @@ LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ) LoRaMacDevEui = mlmeRequest->Req.Join.DevEui; LoRaMacAppEui = mlmeRequest->Req.Join.AppEui; LoRaMacAppKey = mlmeRequest->Req.Join.AppKey; - //LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, mlmeRequest->Req.Join.Datarate ); MaxJoinRequestTrials = mlmeRequest->Req.Join.NbTrials; // Reset variable JoinRequestTrials @@ -3384,12 +3369,12 @@ LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ) macHdr.Value = 0; macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; - //ResetMacParameters( ); + ResetMacParameters( ); altDr.NbTrials = JoinRequestTrials + 1; // Pycom: Add Join DR selection as and when the region permits - RegionForceJoinDataRate( LoRaMacRegion, mlmeRequest->Req.Join.Datarate, &altDr ); + RegionForceJoinDataRate( LoRaMacRegion, mlmeRequest->Req.Join.DR, &altDr ); LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, &altDr ); @@ -3442,14 +3427,15 @@ LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest ) uint8_t fPort = 0; void *fBuffer; uint16_t fBufferSize; - int8_t datarate = DR_0; + int8_t datarate; bool readyToSend = false; if( mcpsRequest == NULL ) { return LORAMAC_STATUS_PARAMETER_INVALID; } - if( LoRaMacState != LORAMAC_IDLE ) + if( ( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) || + ( ( LoRaMacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED ) ) { return LORAMAC_STATUS_BUSY; } diff --git a/lib/lora/mac/LoRaMac.h b/lib/lora/mac/LoRaMac.h index 5c9af97c1b..b53b5b41d1 100644 --- a/lib/lora/mac/LoRaMac.h +++ b/lib/lora/mac/LoRaMac.h @@ -263,30 +263,6 @@ typedef struct sRx2ChannelParams uint8_t Datarate; }Rx2ChannelParams_t; -/*! - * LoRaMAC receive window enumeration - */ -typedef enum eLoRaMacRxSlot -{ - /*! - * LoRaMAC receive window 1 - */ - RX_SLOT_WIN_1, - /*! - * LoRaMAC receive window 2 - */ - RX_SLOT_WIN_2, - /*! - * LoRaMAC receive window 2 for class c - continuous listening - */ - RX_SLOT_WIN_CLASS_C, - /*! - * LoRaMAC class b ping slot window - */ - RX_SLOT_WIN_PING_SLOT -}LoRaMacRxSlot_t; - - /*! * Global MAC layer parameters */ @@ -921,10 +897,6 @@ typedef struct sMcpsConfirm * The uplink frequency related to the frame */ uint32_t UpLinkFrequency; - /*! - * The uplink channel related to the frame - */ - uint32_t Channel; }McpsConfirm_t; /*! @@ -1077,9 +1049,9 @@ typedef struct sMlmeReqJoin */ uint8_t NbTrials; /*! - * Datarate used for join request. + * To support initial data rate selection for the Join Request */ - uint8_t Datarate; + uint8_t DR; }MlmeReqJoin_t; /*! @@ -1419,14 +1391,7 @@ typedef enum eMib * The formula is: * radioTxPower = ( int8_t )floor( maxEirp - antennaGain ) */ - MIB_ANTENNA_GAIN, - /*! - * Default antenna gain of the node. Default value is region specific. - * The antenna gain is used to calculate the TX power of the node. - * The formula is: - * radioTxPower = ( int8_t )floor( maxEirp - antennaGain ) - */ - MIB_DEFAULT_ANTENNA_GAIN + MIB_ANTENNA_GAIN }Mib_t; /*! @@ -1620,12 +1585,6 @@ typedef union uMibParam * Related MIB type: \ref MIB_ANTENNA_GAIN */ float AntennaGain; - /*! - * Default antenna gain - * - * Related MIB type: \ref MIB_DEFAULT_ANTENNA_GAIN - */ - float DefaultAntennaGain; }MibParam_t; /*! @@ -1708,19 +1667,7 @@ typedef enum eLoRaMacStatus * Service not started - the specified region is not supported * or not activated with preprocessor definitions. */ - LORAMAC_STATUS_REGION_NOT_SUPPORTED, - /*! - * - */ - LORAMAC_STATUS_DUTYCYCLE_RESTRICTED, - /*! - * - */ - LORAMAC_STATUS_NO_CHANNEL_FOUND, - /*! - * - */ - LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND + LORAMAC_STATUS_REGION_NOT_SUPPORTED }LoRaMacStatus_t; /*! diff --git a/lib/lora/mac/region/Region.h b/lib/lora/mac/region/Region.h index f426391f24..641f6af810 100644 --- a/lib/lora/mac/region/Region.h +++ b/lib/lora/mac/region/Region.h @@ -672,12 +672,7 @@ typedef enum eInitType /*! * Restores default channels only. */ - INIT_TYPE_RESTORE, - /*! - * Initializes the region specific data to the defaults which were set by - * the application. - */ - INIT_TYPE_APP_DEFAULTS + INIT_TYPE_RESTORE }InitType_t; typedef enum eChannelsMask @@ -907,9 +902,9 @@ typedef struct sRxConfigParams */ bool RxContinuous; /*! - * Sets the RX window. + * Sets the RX window. 0: RX window 1, 1: RX window 2. */ - LoRaMacRxSlot_t RxSlot; + bool Window; }RxConfigParams_t; /*! diff --git a/lib/lora/mac/region/RegionAS923.c b/lib/lora/mac/region/RegionAS923.c index 3f977820fb..bfc36038d9 100644 --- a/lib/lora/mac/region/RegionAS923.c +++ b/lib/lora/mac/region/RegionAS923.c @@ -368,12 +368,6 @@ void RegionAS923InitDefaults( InitType_t type ) ChannelsMask[0] |= ChannelsDefaultMask[0]; break; } - case INIT_TYPE_APP_DEFAULTS: - { - // Update the channels mask defaults - RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); - break; - } default: { break; @@ -607,7 +601,7 @@ bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->RxSlot == 0 ) + if( rxConfig->Window == 0 ) { // Apply window 1 frequency frequency = Channels[rxConfig->Channel].Frequency; diff --git a/lib/lora/mac/region/RegionAU915.c b/lib/lora/mac/region/RegionAU915.c index fe0eb261b4..e1a63135cb 100644 --- a/lib/lora/mac/region/RegionAU915.c +++ b/lib/lora/mac/region/RegionAU915.c @@ -338,15 +338,6 @@ void RegionAU915InitDefaults( InitType_t type ) } break; } - case INIT_TYPE_APP_DEFAULTS: - { - // Copy channels default mask - RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); - - // Copy into channels mask remaining - RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 ); - break; - } default: { break; @@ -523,7 +514,7 @@ bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->RxSlot == 0 ) + if( rxConfig->Window == 0 ) { // Apply window 1 frequency frequency = AU915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * AU915_STEPWIDTH_RX1_CHANNEL; diff --git a/lib/lora/mac/region/RegionCN470.c b/lib/lora/mac/region/RegionCN470.c index a3bbd10149..929646c504 100644 --- a/lib/lora/mac/region/RegionCN470.c +++ b/lib/lora/mac/region/RegionCN470.c @@ -318,12 +318,6 @@ void RegionCN470InitDefaults( InitType_t type ) RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); break; } - case INIT_TYPE_APP_DEFAULTS: - { - // Update the channels mask defaults - RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); - break; - } default: { break; @@ -483,7 +477,7 @@ bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->RxSlot == 0 ) + if( rxConfig->Window == 0 ) { // Apply window 1 frequency frequency = CN470_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 48 ) * CN470_STEPWIDTH_RX1_CHANNEL; diff --git a/lib/lora/mac/region/RegionEU868.c b/lib/lora/mac/region/RegionEU868.c index 7f0c69b049..da31e1805f 100644 --- a/lib/lora/mac/region/RegionEU868.c +++ b/lib/lora/mac/region/RegionEU868.c @@ -359,12 +359,6 @@ void RegionEU868InitDefaults( InitType_t type ) ChannelsMask[0] |= ChannelsDefaultMask[0]; break; } - case INIT_TYPE_APP_DEFAULTS: - { - // Update the channels mask defaults - RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); - break; - } default: { break; @@ -579,7 +573,7 @@ bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->RxSlot == 0 ) + if( rxConfig->Window == 0 ) { // Apply window 1 frequency frequency = Channels[rxConfig->Channel].Frequency; @@ -953,7 +947,7 @@ bool RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; *time = 0; - return LORAMAC_STATUS_OK; + return true; } else { @@ -961,12 +955,12 @@ bool RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, { // Delay transmission due to AggregatedTimeOff or to a band time off *time = nextTxDelay; - return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + return true; } // Datarate not supported by any channel, restore defaults ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); *time = 0; - return LORAMAC_STATUS_NO_CHANNEL_FOUND; + return false; } } diff --git a/lib/lora/mac/region/RegionIN865.c b/lib/lora/mac/region/RegionIN865.c index 07f55e16c4..bf85248ac5 100644 --- a/lib/lora/mac/region/RegionIN865.c +++ b/lib/lora/mac/region/RegionIN865.c @@ -334,12 +334,6 @@ void RegionIN865InitDefaults( InitType_t type ) ChannelsMask[0] |= ChannelsDefaultMask[0]; break; } - case INIT_TYPE_APP_DEFAULTS: - { - // Update the channels mask defaults - RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); - break; - } default: { break; @@ -353,14 +347,7 @@ bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) { case PHY_TX_DR: { - if( verify->DatarateParams.Datarate == DR_6 ) - {// DR_6 is not supported by this region - return false; - } - else - { - return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ); - } + return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ); } case PHY_DEF_TX_DR: { @@ -368,14 +355,7 @@ bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) } case PHY_RX_DR: { - if( verify->DatarateParams.Datarate == DR_6 ) - {// DR_6 is not supported by this region - return false; - } - else - { - return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ); - } + return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ); } case PHY_DEF_TX_POWER: case PHY_TX_POWER: @@ -567,7 +547,7 @@ bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->RxSlot == 0 ) + if( rxConfig->Window == 0 ) { // Apply window 1 frequency frequency = Channels[rxConfig->Channel].Frequency; @@ -705,36 +685,29 @@ uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, in } } - if( linkAdrParams.Datarate != DR_6 ) - { - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; - phyParam = RegionIN865GetPhyParam( &getPhy ); - - linkAdrVerifyParams.Status = status; - linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; - linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; - linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; - linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; - linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; - linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; - linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; - linkAdrVerifyParams.NbChannels = IN865_MAX_NB_CHANNELS; - linkAdrVerifyParams.ChannelsMask = &chMask; - linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; - linkAdrVerifyParams.MaxDatarate = IN865_TX_MAX_DATARATE; - linkAdrVerifyParams.Channels = Channels; - linkAdrVerifyParams.MinTxPower = IN865_MIN_TX_POWER; - linkAdrVerifyParams.MaxTxPower = IN865_MAX_TX_POWER; - - // Verify the parameters and update, if necessary - status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); - } - else - {// DR_6 is not supported by this region - status &= 0xFD; // Datarate KO - } + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionIN865GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = IN865_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = IN865_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = Channels; + linkAdrVerifyParams.MinTxPower = IN865_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = IN865_MAX_TX_POWER; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); // Update channelsMask if everything is correct if( status == 0x07 ) diff --git a/lib/lora/mac/region/RegionKR920.c b/lib/lora/mac/region/RegionKR920.c index e4785eddba..8377efee3e 100644 --- a/lib/lora/mac/region/RegionKR920.c +++ b/lib/lora/mac/region/RegionKR920.c @@ -353,12 +353,6 @@ void RegionKR920InitDefaults( InitType_t type ) ChannelsMask[0] |= ChannelsDefaultMask[0]; break; } - case INIT_TYPE_APP_DEFAULTS: - { - // Update the channels mask defaults - RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 1 ); - break; - } default: { break; @@ -564,7 +558,7 @@ bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->RxSlot == 0 ) + if( rxConfig->Window == 0 ) { // Apply window 1 frequency frequency = Channels[rxConfig->Channel].Frequency; diff --git a/lib/lora/mac/region/RegionUS915-Hybrid.c b/lib/lora/mac/region/RegionUS915-Hybrid.c index a13915ef3b..06ca70dad1 100644 --- a/lib/lora/mac/region/RegionUS915-Hybrid.c +++ b/lib/lora/mac/region/RegionUS915-Hybrid.c @@ -168,7 +168,7 @@ static bool ValidateChannelsMask( uint16_t* channelsMask ) block1 = channelsMaskCpy[i] & 0x00FF; block2 = channelsMaskCpy[i] & 0xFF00; - if( CountBits( block1, 16 ) > 1 ) + if( CountBits( block1, 16 ) > 5 ) { channelsMaskCpy[i] &= block1; channelsMaskCpy[4] = 1 << ( i * 2 ); @@ -176,7 +176,7 @@ static bool ValidateChannelsMask( uint16_t* channelsMask ) index = i; break; } - else if( CountBits( block2, 16 ) > 1 ) + else if( CountBits( block2, 16 ) > 5 ) { channelsMaskCpy[i] &= block2; channelsMaskCpy[4] = 1 << ( i * 2 + 1 ); @@ -431,16 +431,6 @@ void RegionUS915HybridInitDefaults( InitType_t type ) { // Copy-And the channels mask ChannelsMaskRemaining[i] &= ChannelsMask[i]; } - break; - } - case INIT_TYPE_APP_DEFAULTS: - { - // Copy channels default mask - RegionCommonChanMaskCopy( ChannelsMask, ChannelsDefaultMask, 6 ); - - // Copy into channels mask remaining - RegionCommonChanMaskCopy( ChannelsMaskRemaining, ChannelsMask, 6 ); - break; } default: { @@ -619,7 +609,7 @@ bool RegionUS915HybridRxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->RxSlot == 0 ) + if( rxConfig->Window == 0 ) { // Apply window 1 frequency frequency = US915_HYBRID_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_HYBRID_STEPWIDTH_RX1_CHANNEL; diff --git a/lib/lora/mac/region/RegionUS915.c b/lib/lora/mac/region/RegionUS915.c index 76c41ce2ce..aa5b8bd91e 100644 --- a/lib/lora/mac/region/RegionUS915.c +++ b/lib/lora/mac/region/RegionUS915.c @@ -522,7 +522,7 @@ bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) return false; } - if( rxConfig->RxSlot == 0 ) + if( rxConfig->Window == 0 ) { // Apply window 1 frequency frequency = US915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_STEPWIDTH_RX1_CHANNEL;