diff --git a/.gitignore b/.gitignore index 4ab1f2a5d..b22a8d404 100644 --- a/.gitignore +++ b/.gitignore @@ -44,5 +44,8 @@ src/.vscode/settings.json examples/Wippersnapper_demo/build/ -#Platformio artifacts +# Platformio artifacts .pio/ + +# Secrets +data/ \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 7a3bc6d24..1ed2b6d54 100644 --- a/platformio.ini +++ b/platformio.ini @@ -82,19 +82,22 @@ lib_deps = ; Common build environment for ESP32 platform [common:esp32] platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.03/platform-espressif32.zip -lib_ignore = WiFiNINA +lib_ignore = WiFiNINA, WiFi101 monitor_filters = esp32_exception_decoder, time ; Common build environment for ESP8266 platform [common:esp8266] platform = espressif8266 -lib_ignore = WiFiNINA, Adafruit TinyUSB Library +lib_ignore = WiFiNINA, WiFi101, Adafruit TinyUSB Library ; Common build environment for Atmel/Microchip SAMDx platform [common:atsamd] platform = atmelsam +platform_packages = + platformio/framework-arduino-samd-adafruit@^1.7.13 + platformio/tool-jlink@^1.78811.0 lib_ldf_mode = deep - +lib_archive = no ; debug timer issues see https://community.platformio.org/t/choose-usb-stack-as-tiny-usb/22451/5 [common:rp2040] platform = https://github.com/maxgerhardt/platform-raspberrypi.git @@ -107,9 +110,12 @@ board_build.core = earlephilhower board_build.filesystem_size = 0.5m build_flags = -DUSE_TINYUSB ; Once https://github.com/platformio/platformio-core > 6.1.11 these can be removed -lib_ignore = WiFiNINA, Adafruit Zero DMA Library +lib_ignore = WiFiNINA, WiFi101, Adafruit Zero DMA Library lib_compat_mode = soft ; can be strict once pio detects SleepyDog on RP2040 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Individual Board Definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; ESP32-x Boards ; ; Adafruit ESP32 Feather @@ -292,14 +298,39 @@ build_flags = -DUSE_TINYUSB=1 [env:adafruit_pyportal_m4_titano] extends = common:atsamd board = adafruit_pyportal_m4_titano -; build_type = debug +build_type = debug +upload_protocol = sam-ba ; upload_protocol = jlink -; debug_tool = jlink +debug_tool = jlink +monitor_port = auto ; monitor_port = jlink -; debug_init_break = +; monitor_port = socket://localhost:19021 +; debug_init_break = tbreak clearConfiguration lib_ignore = USBHost -build_flags = -DUSE_TINYUSB=1 - -DADAFRUIT_PYPORTAL_M4_TITANO +build_flags = -DUSE_TINYUSB + -D__SAMD51J20A__ + -DCRYSTALLESS + -DADAFRUIT_PYPORTAL_M4_TITANO + -D__SAMD51__ + -D__FPU_PRESENT + -DARM_MATH_CM4 + -mfloat-abi=hard + -mfpu=fpv4-sp-d16 + -DCORE_DEBUG_LEVEL=5 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DCFG_TUSB_DEBUG=1 + -DDEBUG=1 + -DNDEBUG=1 + -DUSE_AIRLIFT=1 + -g + ; -DUSBCON + ; -UCDC_DISABLED + ; -UPLUGGABLE_USB_DISABLED + ; -DCDC_ENABLED + ; -DPLUGGABLE_USB_ENABLED + ; -DSERIAL_DEBUG=1 + ; -DSERIAL_PORT=Serial1 + ; -DSERCOM_INSTANCE_SERIAL=1 ; Adafruit Metro M4 Airlift Lite [env:adafruit_metro_m4_airliftlite] diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 72896b288..a4c190825 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -182,6 +182,17 @@ void Wippersnapper::getMacAddr() { WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); } +/****************************************************************************/ +/*! + @brief Gets the network's RSSI. + @return int32_t RSSI value, 0 to 255, in dB +*/ +/****************************************************************************/ +int32_t Wippersnapper::getRSSI() { + WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); + return 0; +} + /****************************************************************************/ /*! @brief Sets up the MQTT client session. @@ -357,8 +368,8 @@ bool cbDecodePinConfigMsg(pb_istream_t *stream, const pb_field_t *field, // pb_decode the stream into a pinReqMessage wippersnapper_pin_v1_ConfigurePinRequest pinReqMsg = wippersnapper_pin_v1_ConfigurePinRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pin_v1_ConfigurePinRequest_fields, - &pinReqMsg)) { + if (!ws_pb_decode(stream, wippersnapper_pin_v1_ConfigurePinRequest_fields, + &pinReqMsg)) { WS_DEBUG_PRINTLN("ERROR: Could not decode CreateSignalRequest") is_success = false; } @@ -398,7 +409,8 @@ bool cbDecodeDigitalPinWriteMsg(pb_istream_t *stream, const pb_field_t *field, // Decode stream into a PinEvent wippersnapper_pin_v1_PinEvent pinEventMsg = wippersnapper_pin_v1_PinEvent_init_zero; - if (!pb_decode(stream, wippersnapper_pin_v1_PinEvent_fields, &pinEventMsg)) { + if (!ws_pb_decode(stream, wippersnapper_pin_v1_PinEvent_fields, + &pinEventMsg)) { WS_DEBUG_PRINTLN("ERROR: Could not decode PinEvents") is_success = false; } @@ -442,8 +454,8 @@ bool cbSignalMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { msg.list.funcs.decode = cbDecodePinConfigMsg; msg.list.arg = field->pData; // decode each ConfigurePinRequest sub-message - if (!pb_decode(stream, wippersnapper_pin_v1_ConfigurePinRequests_fields, - &msg)) { + if (!ws_pb_decode(stream, wippersnapper_pin_v1_ConfigurePinRequests_fields, + &msg)) { WS_DEBUG_PRINTLN("ERROR: Could not decode CreateSignalRequest") is_success = false; WS.pinCfgCompleted = false; @@ -463,7 +475,7 @@ bool cbSignalMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { msg.list.funcs.decode = cbDecodeDigitalPinWriteMsg; msg.list.arg = field->pData; // decode each PinEvents sub-message - if (!pb_decode(stream, wippersnapper_pin_v1_PinEvents_fields, &msg)) { + if (!ws_pb_decode(stream, wippersnapper_pin_v1_PinEvents_fields, &msg)) { WS_DEBUG_PRINTLN("ERROR: Could not decode CreateSign2alRequest") is_success = false; } @@ -496,8 +508,8 @@ bool Wippersnapper::decodeSignalMsg( // decode the CreateSignalRequest, calls cbSignalMessage and assoc. callbacks pb_istream_t stream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, - encodedSignalMsg)) { + if (!ws_pb_decode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, + encodedSignalMsg)) { WS_DEBUG_PRINTLN( "ERROR (decodeSignalMsg):, Could not decode CreateSignalRequest") is_success = false; @@ -546,8 +558,12 @@ void publishI2CResponse(wippersnapper_signal_v1_I2CResponse *msgi2cResponse) { pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_I2CResponse_fields, msgi2cResponse); WS_DEBUG_PRINT("Publishing Message: I2CResponse..."); - WS._mqtt->publish(WS._topic_signal_i2c_device, WS._buffer_outgoing, msgSz, 1); - WS_DEBUG_PRINTLN("Published!"); + if (!WS._mqtt->publish(WS._topic_signal_i2c_device, WS._buffer_outgoing, + msgSz, 1)) { + WS_DEBUG_PRINTLN("ERROR: Failed to publish I2C Response!"); + } else { + WS_DEBUG_PRINTLN("Published!"); + } } /******************************************************************************************/ @@ -562,8 +578,8 @@ bool encodeI2CResponse(wippersnapper_signal_v1_I2CResponse *msgi2cResponse) { memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_I2CResponse_fields, - msgi2cResponse)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_I2CResponse_fields, + msgi2cResponse)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode I2C response message!"); return false; } @@ -609,8 +625,8 @@ bool cbDecodeI2CDeviceInitRequestList(pb_istream_t *stream, // Decode stream into individual msgI2CDeviceInitRequest messages wippersnapper_i2c_v1_I2CDeviceInitRequest msgI2CDeviceInitRequest = wippersnapper_i2c_v1_I2CDeviceInitRequest_init_zero; - if (!pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequest_fields, - &msgI2CDeviceInitRequest)) { + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequest_fields, + &msgI2CDeviceInitRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode I2CDeviceInitRequest message."); return false; } @@ -682,8 +698,8 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, // Decode I2CBusScanRequest wippersnapper_i2c_v1_I2CBusScanRequest msgScanReq = wippersnapper_i2c_v1_I2CBusScanRequest_init_zero; - if (!pb_decode(stream, wippersnapper_i2c_v1_I2CBusScanRequest_fields, - &msgScanReq)) { + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CBusScanRequest_fields, + &msgScanReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_i2c_v1_I2CBusScanRequest"); return false; // fail out if we can't decode the request @@ -735,8 +751,8 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, cbDecodeI2CDeviceInitRequestList; msgI2CDeviceInitRequestList.list.arg = field->pData; // Decode each sub-message - if (!pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequests_fields, - &msgI2CDeviceInitRequestList)) { + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequests_fields, + &msgI2CDeviceInitRequestList)) { WS_DEBUG_PRINTLN("ERROR: Could not decode I2CDeviceInitRequests"); is_success = false; } @@ -751,8 +767,8 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, wippersnapper_i2c_v1_I2CDeviceInitRequest msgI2CDeviceInitRequest = wippersnapper_i2c_v1_I2CDeviceInitRequest_init_zero; // Decode stream into struct, msgI2CDeviceInitRequest - if (!pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequest_fields, - &msgI2CDeviceInitRequest)) { + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequest_fields, + &msgI2CDeviceInitRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode I2CDeviceInitRequest message."); return false; // fail out if we can't decode } @@ -797,8 +813,9 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, wippersnapper_i2c_v1_I2CDeviceUpdateRequest_init_zero; // Decode stream into message - if (!pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceUpdateRequest_fields, - &msgI2CDeviceUpdateRequest)) { + if (!ws_pb_decode(stream, + wippersnapper_i2c_v1_I2CDeviceUpdateRequest_fields, + &msgI2CDeviceUpdateRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode I2CDeviceUpdateRequest message."); return false; // fail out if we can't decode @@ -829,8 +846,9 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, wippersnapper_i2c_v1_I2CDeviceDeinitRequest msgI2CDeviceDeinitRequest = wippersnapper_i2c_v1_I2CDeviceDeinitRequest_init_zero; // Decode stream into struct, msgI2CDeviceDeinitRequest - if (!pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceDeinitRequest_fields, - &msgI2CDeviceDeinitRequest)) { + if (!ws_pb_decode(stream, + wippersnapper_i2c_v1_I2CDeviceDeinitRequest_fields, + &msgI2CDeviceDeinitRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode I2CDeviceDeinitRequest message."); return false; // fail out if we can't decode @@ -891,8 +909,8 @@ void cbSignalI2CReq(char *data, uint16_t len) { // Decode I2C signal request pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&istream, wippersnapper_signal_v1_I2CRequest_fields, - &WS.msgSignalI2C)) + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_I2CRequest_fields, + &WS.msgSignalI2C)) WS_DEBUG_PRINTLN("ERROR: Unable to decode I2C message"); } @@ -917,8 +935,8 @@ bool cbDecodeServoMsg(pb_istream_t *stream, const pb_field_t *field, // Attempt to decode contents of servo_attach message wippersnapper_servo_v1_ServoAttachRequest msgServoAttachReq = wippersnapper_servo_v1_ServoAttachRequest_init_zero; - if (!pb_decode(stream, wippersnapper_servo_v1_ServoAttachRequest_fields, - &msgServoAttachReq)) { + if (!ws_pb_decode(stream, wippersnapper_servo_v1_ServoAttachRequest_fields, + &msgServoAttachReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_servo_v1_ServoAttachRequest"); #ifdef USE_DISPLAY @@ -969,8 +987,8 @@ bool cbDecodeServoMsg(pb_istream_t *stream, const pb_field_t *field, memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_ServoResponse_fields, - &msgServoResp)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_ServoResponse_fields, + &msgServoResp)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode servo response message!"); return false; } @@ -988,8 +1006,8 @@ bool cbDecodeServoMsg(pb_istream_t *stream, const pb_field_t *field, wippersnapper_servo_v1_ServoWriteRequest msgServoWriteReq = wippersnapper_servo_v1_ServoWriteRequest_init_zero; - if (!pb_decode(stream, wippersnapper_servo_v1_ServoWriteRequest_fields, - &msgServoWriteReq)) { + if (!ws_pb_decode(stream, wippersnapper_servo_v1_ServoWriteRequest_fields, + &msgServoWriteReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_servo_v1_ServoWriteRequest"); return false; // fail out if we can't decode the request @@ -1018,8 +1036,8 @@ bool cbDecodeServoMsg(pb_istream_t *stream, const pb_field_t *field, // Attempt to decode contents of servo detach message wippersnapper_servo_v1_ServoDetachRequest msgServoDetachReq = wippersnapper_servo_v1_ServoDetachRequest_init_zero; - if (!pb_decode(stream, wippersnapper_servo_v1_ServoDetachRequest_fields, - &msgServoDetachReq)) { + if (!ws_pb_decode(stream, wippersnapper_servo_v1_ServoDetachRequest_fields, + &msgServoDetachReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_servo_v1_ServoDetachRequest"); return false; // fail out if we can't decode the request @@ -1071,8 +1089,8 @@ void cbServoMsg(char *data, uint16_t len) { // Decode servo message from buffer pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&istream, wippersnapper_signal_v1_ServoRequest_fields, - &WS.msgServo)) + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_ServoRequest_fields, + &WS.msgServo)) WS_DEBUG_PRINTLN("ERROR: Unable to decode servo message"); } @@ -1096,8 +1114,8 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { // Attempt to decode contents of PWM attach message wippersnapper_pwm_v1_PWMAttachRequest msgPWMAttachRequest = wippersnapper_pwm_v1_PWMAttachRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pwm_v1_PWMAttachRequest_fields, - &msgPWMAttachRequest)) { + if (!ws_pb_decode(stream, wippersnapper_pwm_v1_PWMAttachRequest_fields, + &msgPWMAttachRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_pwm_v1_PWMAttachRequest"); #ifdef USE_DISPLAY @@ -1134,8 +1152,8 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_PWMResponse_fields, - &msgPWMResponse)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_PWMResponse_fields, + &msgPWMResponse)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode PWM response message!"); return false; } @@ -1143,8 +1161,11 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_PWMResponse_fields, &msgPWMResponse); WS_DEBUG_PRINT("PUBLISHING: PWM Attach Response..."); - WS._mqtt->publish(WS._topic_signal_pwm_device, WS._buffer_outgoing, msgSz, - 1); + if (!WS._mqtt->publish(WS._topic_signal_pwm_device, WS._buffer_outgoing, + msgSz, 1)) { + WS_DEBUG_PRINTLN("ERROR: Failed to publish PWM Attach Response!"); + return false; + } WS_DEBUG_PRINTLN("Published!"); #ifdef USE_DISPLAY @@ -1160,8 +1181,8 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { // Attempt to decode contents of PWM detach message wippersnapper_pwm_v1_PWMDetachRequest msgPWMDetachRequest = wippersnapper_pwm_v1_PWMDetachRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pwm_v1_PWMDetachRequest_fields, - &msgPWMDetachRequest)) { + if (!ws_pb_decode(stream, wippersnapper_pwm_v1_PWMDetachRequest_fields, + &msgPWMDetachRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_pwm_v1_PWMDetachRequest"); #ifdef USE_DISPLAY @@ -1187,8 +1208,9 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { // Attempt to decode contents of PWM detach message wippersnapper_pwm_v1_PWMWriteFrequencyRequest msgPWMWriteFreqRequest = wippersnapper_pwm_v1_PWMWriteFrequencyRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pwm_v1_PWMWriteFrequencyRequest_fields, - &msgPWMWriteFreqRequest)) { + if (!ws_pb_decode(stream, + wippersnapper_pwm_v1_PWMWriteFrequencyRequest_fields, + &msgPWMWriteFreqRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode " "wippersnapper_pwm_v1_PWMWriteFrequencyRequest"); #ifdef USE_DISPLAY @@ -1220,8 +1242,9 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { // Attempt to decode contents of PWM detach message wippersnapper_pwm_v1_PWMWriteDutyCycleRequest msgPWMWriteDutyCycleRequest = wippersnapper_pwm_v1_PWMWriteDutyCycleRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pwm_v1_PWMWriteDutyCycleRequest_fields, - &msgPWMWriteDutyCycleRequest)) { + if (!ws_pb_decode(stream, + wippersnapper_pwm_v1_PWMWriteDutyCycleRequest_fields, + &msgPWMWriteDutyCycleRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode " "wippersnapper_pwm_v1_PWMWriteDutyCycleRequest"); #ifdef USE_DISPLAY @@ -1275,8 +1298,8 @@ void cbPWMMsg(char *data, uint16_t len) { // Decode servo message from buffer pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&istream, wippersnapper_signal_v1_PWMRequest_fields, - &WS.msgPWM)) + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_PWMRequest_fields, + &WS.msgPWM)) WS_DEBUG_PRINTLN("ERROR: Unable to decode PWM message"); } @@ -1303,8 +1326,9 @@ bool cbDecodeDs18x20Msg(pb_istream_t *stream, const pb_field_t *field, wippersnapper_ds18x20_v1_Ds18x20InitRequest msgDS18xInitReq = wippersnapper_ds18x20_v1_Ds18x20InitRequest_init_zero; - if (!pb_decode(stream, wippersnapper_ds18x20_v1_Ds18x20InitRequest_fields, - &msgDS18xInitReq)) { + if (!ws_pb_decode(stream, + wippersnapper_ds18x20_v1_Ds18x20InitRequest_fields, + &msgDS18xInitReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode " "wippersnapper_ds18x20_v1_Ds18x20InitRequest"); return false; // fail out if we can't decode the request @@ -1319,8 +1343,9 @@ bool cbDecodeDs18x20Msg(pb_istream_t *stream, const pb_field_t *field, // Attempt to decode contents of message wippersnapper_ds18x20_v1_Ds18x20DeInitRequest msgDS18xDeInitReq = wippersnapper_ds18x20_v1_Ds18x20DeInitRequest_init_zero; - if (!pb_decode(stream, wippersnapper_ds18x20_v1_Ds18x20DeInitRequest_fields, - &msgDS18xDeInitReq)) { + if (!ws_pb_decode(stream, + wippersnapper_ds18x20_v1_Ds18x20DeInitRequest_fields, + &msgDS18xDeInitReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode " "wippersnapper_ds18x20_v1_Ds18x20DeInitRequest"); return false; // fail out if we can't decode the request @@ -1363,8 +1388,8 @@ void cbSignalDSReq(char *data, uint16_t len) { // Decode DS signal request pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&istream, wippersnapper_signal_v1_Ds18x20Request_fields, - &WS.msgSignalDS)) + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_Ds18x20Request_fields, + &WS.msgSignalDS)) WS_DEBUG_PRINTLN("ERROR: Unable to decode DS message"); } @@ -1393,8 +1418,9 @@ bool cbDecodePixelsMsg(pb_istream_t *stream, const pb_field_t *field, // attempt to decode create message wippersnapper_pixels_v1_PixelsCreateRequest msgPixelsCreateReq = wippersnapper_pixels_v1_PixelsCreateRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pixels_v1_PixelsCreateRequest_fields, - &msgPixelsCreateReq)) { + if (!ws_pb_decode(stream, + wippersnapper_pixels_v1_PixelsCreateRequest_fields, + &msgPixelsCreateReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode message of type " "wippersnapper_pixels_v1_PixelsCreateRequest!"); #ifdef USE_DISPLAY @@ -1414,8 +1440,9 @@ bool cbDecodePixelsMsg(pb_istream_t *stream, const pb_field_t *field, // attempt to decode delete strand message wippersnapper_pixels_v1_PixelsDeleteRequest msgPixelsDeleteReq = wippersnapper_pixels_v1_PixelsDeleteRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pixels_v1_PixelsDeleteRequest_fields, - &msgPixelsDeleteReq)) { + if (!ws_pb_decode(stream, + wippersnapper_pixels_v1_PixelsDeleteRequest_fields, + &msgPixelsDeleteReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode message of type " "wippersnapper_pixels_v1_PixelsDeleteRequest!"); return false; @@ -1432,8 +1459,8 @@ bool cbDecodePixelsMsg(pb_istream_t *stream, const pb_field_t *field, // attempt to decode pixel write message wippersnapper_pixels_v1_PixelsWriteRequest msgPixelsWritereq = wippersnapper_pixels_v1_PixelsWriteRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pixels_v1_PixelsWriteRequest_fields, - &msgPixelsWritereq)) { + if (!ws_pb_decode(stream, wippersnapper_pixels_v1_PixelsWriteRequest_fields, + &msgPixelsWritereq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode message of type " "wippersnapper_pixels_v1_PixelsWriteRequest!"); return false; @@ -1474,8 +1501,8 @@ void cbPixelsMsg(char *data, uint16_t len) { // Decode pixel message from buffer pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&istream, wippersnapper_signal_v1_PixelsRequest_fields, - &WS.msgPixels)) + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_PixelsRequest_fields, + &WS.msgPixels)) WS_DEBUG_PRINTLN("ERROR: Unable to decode pixel topic message"); } @@ -1503,8 +1530,9 @@ bool cbDecodeUARTMessage(pb_istream_t *stream, const pb_field_t *field, // attempt to decode create message wippersnapper_uart_v1_UARTDeviceAttachRequest msgUARTInitReq = wippersnapper_uart_v1_UARTDeviceAttachRequest_init_zero; - if (!pb_decode(stream, wippersnapper_uart_v1_UARTDeviceAttachRequest_fields, - &msgUARTInitReq)) { + if (!ws_pb_decode(stream, + wippersnapper_uart_v1_UARTDeviceAttachRequest_fields, + &msgUARTInitReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode message of type: UARTDeviceAttachRequest!"); return false; @@ -1534,8 +1562,8 @@ bool cbDecodeUARTMessage(pb_istream_t *stream, const pb_field_t *field, memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_UARTResponse_fields, - &msgUARTResponse)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_UARTResponse_fields, + &msgUARTResponse)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode UART response message!"); return false; } @@ -1543,8 +1571,11 @@ bool cbDecodeUARTMessage(pb_istream_t *stream, const pb_field_t *field, pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_UARTResponse_fields, &msgUARTResponse); WS_DEBUG_PRINT("PUBLISHING: UART Attach Response..."); - WS._mqtt->publish(WS._topic_signal_uart_device, WS._buffer_outgoing, msgSz, - 1); + if (!WS._mqtt->publish(WS._topic_signal_uart_device, WS._buffer_outgoing, + msgSz, 1)) { + WS_DEBUG_PRINTLN("ERROR: Failed to publish UART Attach Response!"); + return false; + } WS_DEBUG_PRINTLN("Published!"); } else if (field->tag == @@ -1553,8 +1584,9 @@ bool cbDecodeUARTMessage(pb_istream_t *stream, const pb_field_t *field, // attempt to decode uart detach request message wippersnapper_uart_v1_UARTDeviceDetachRequest msgUARTDetachReq = wippersnapper_uart_v1_UARTDeviceDetachRequest_init_zero; - if (!pb_decode(stream, wippersnapper_uart_v1_UARTDeviceDetachRequest_fields, - &msgUARTDetachReq)) { + if (!ws_pb_decode(stream, + wippersnapper_uart_v1_UARTDeviceDetachRequest_fields, + &msgUARTDetachReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode message!"); return false; } @@ -1594,8 +1626,8 @@ void cbSignalUARTReq(char *data, uint16_t len) { // Decode DS signal request pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&istream, wippersnapper_signal_v1_UARTRequest_fields, - &WS.msgSignalUART)) + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_UARTRequest_fields, + &WS.msgSignalUART)) WS_DEBUG_PRINTLN("ERROR: Unable to decode UART Signal message"); } @@ -1624,8 +1656,8 @@ bool Wippersnapper::encodePinEvent( // Encode signal message pb_ostream_t stream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, - outgoingSignalMsg)) { + if (!ws_pb_encode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, + outgoingSignalMsg)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode signal message"); is_success = false; } @@ -2370,7 +2402,6 @@ void Wippersnapper::runNetFSM() { haltError("ERROR: Unable to find WiFi network, rebooting soon...", WS_LED_STATUS_WIFI_CONNECTING); } - WS_DEBUG_PRINTLN("SSID found!"); // Attempt to connect to wireless network maxAttempts = 5; while (maxAttempts > 0) { @@ -2527,10 +2558,17 @@ void Wippersnapper::pingBroker() { // ping within keepalive-10% to keep connection open if (millis() > (_prv_ping + (WS_KEEPALIVE_INTERVAL_MS - (WS_KEEPALIVE_INTERVAL_MS * 0.10)))) { - WS_DEBUG_PRINTLN("PING!"); - // TODO: Add back, is crashing currently - WS._mqtt->ping(); + WS_DEBUG_PRINT("Sending MQTT PING: "); + if (WS._mqtt->ping()) { + WS_DEBUG_PRINTLN("SUCCESS!"); + } else { + WS_DEBUG_PRINTLN("FAILURE! Running network FSM..."); + WS._mqtt->disconnect(); + runNetFSM(); + } _prv_ping = millis(); + WS_DEBUG_PRINT("WiFi RSSI: "); + WS_DEBUG_PRINTLN(getRSSI()); } // blink status LED every STATUS_LED_KAT_BLINK_TIME millis if (millis() > (_prvKATBlink + STATUS_LED_KAT_BLINK_TIME)) { @@ -2601,7 +2639,9 @@ void Wippersnapper::publish(const char *topic, uint8_t *payload, uint16_t bLen, // runNetFSM(); // NOTE: Removed for now, causes error with virtual _connect // method when caused with WS object in another file. WS.feedWDT(); - WS._mqtt->publish(topic, payload, bLen, qos); + if (!WS._mqtt->publish(topic, payload, bLen, qos)) { + WS_DEBUG_PRINTLN("Failed to publish MQTT message!"); + } } /**************************************************************/ @@ -2792,9 +2832,9 @@ void Wippersnapper::publishPinConfigComplete() { pb_ostream_t _msg_stream = pb_ostream_from_buffer(_message_buffer, sizeof(_message_buffer)); - bool _status = - pb_encode(&_msg_stream, - wippersnapper_description_v1_RegistrationComplete_fields, &msg); + bool _status = ws_pb_encode( + &_msg_stream, wippersnapper_description_v1_RegistrationComplete_fields, + &msg); size_t _message_len = _msg_stream.bytes_written; // verify message encoded correctly @@ -2838,9 +2878,11 @@ ws_status_t Wippersnapper::run() { // Process DS18x20 sensor events WS._ds18x20Component->update(); + WS.feedWDT(); // Process UART sensor events WS._uartComponent->update(); + WS.feedWDT(); return WS_NET_CONNECTED; // TODO: Make this funcn void! -} \ No newline at end of file +} diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index e57881ed2..fc807290f 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -59,6 +59,57 @@ {} ///< Prints line from debug output. #endif +#define WS_DELAY_WITH_WDT(timeout) \ + { \ + unsigned long start = millis(); \ + while (millis() - start < timeout) { \ + delay(10); \ + yield(); \ + feedWDT(); \ + if (millis() < start) { \ + start = millis(); /* if rollover */ \ + } \ + } \ + } ///< Delay function + +/**************************************************************************/ +/*! + @brief Retry a function until a condition is met or a timeout is reached. + @param func + The function to retry. + @param result_type + The type of the result of the function. + @param result_var + The variable to store the last result of the function. + @param condition + The condition to check the result against. + @param timeout + The maximum time to retry the function. + @param interval + The time to wait between retries. + @param ... + The arguments to pass to the function. +*/ +/**************************************************************************/ +#define RETRY_FUNCTION_UNTIL_TIMEOUT(func, result_type, result_var, condition, \ + timeout, interval, ...) \ + { \ + unsigned long startTime = millis(); \ + while (millis() - startTime < timeout) { \ + result_type result_var = func(__VA_ARGS__); \ + if (condition(result_var)) { \ + break; \ + } \ + if (startTime > millis()) { \ + startTime = millis(); /* if rollover */ \ + } \ + WS_DELAY_WITH_WDT(interval); \ + } \ + } ///< Retry a function until a condition is met or a timeout is reached. + +// Wippersnapper pb helpers +#include + // Wippersnapper components #include "components/analogIO/Wippersnapper_AnalogIO.h" #include "components/digitalIO/Wippersnapper_DigitalGPIO.h" @@ -220,6 +271,7 @@ class Wippersnapper { void disconnect(); virtual void getMacAddr(); + virtual int32_t getRSSI(); virtual void setupMQTTClient(const char *clientID); virtual ws_status_t networkStatus(); diff --git a/src/components/analogIO/Wippersnapper_AnalogIO.cpp b/src/components/analogIO/Wippersnapper_AnalogIO.cpp index 86ceebced..a384cdba6 100644 --- a/src/components/analogIO/Wippersnapper_AnalogIO.cpp +++ b/src/components/analogIO/Wippersnapper_AnalogIO.cpp @@ -280,8 +280,8 @@ bool Wippersnapper_AnalogIO::encodePinEvent( // Encode signal message pb_ostream_t stream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, - &outgoingSignalMsg)) { + if (!ws_pb_encode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, + &outgoingSignalMsg)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode signal message"); return false; } diff --git a/src/components/ds18x20/ws_ds18x20.cpp b/src/components/ds18x20/ws_ds18x20.cpp index 91360271d..0cfa6e0c5 100644 --- a/src/components/ds18x20/ws_ds18x20.cpp +++ b/src/components/ds18x20/ws_ds18x20.cpp @@ -108,8 +108,8 @@ bool ws_ds18x20::addDS18x20( memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_Ds18x20Response_fields, - &msgInitResp)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_Ds18x20Response_fields, + &msgInitResp)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode msg_init response message!"); return false; } @@ -257,9 +257,9 @@ void ws_ds18x20::update() { memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer( WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, - wippersnapper_signal_v1_Ds18x20Response_fields, - &msgDS18x20Response)) { + if (!ws_pb_encode(&ostream, + wippersnapper_signal_v1_Ds18x20Response_fields, + &msgDS18x20Response)) { WS_DEBUG_PRINTLN( "ERROR: Unable to encode DS18x20 event responsemessage!"); snprintf(buffer, 100, @@ -296,6 +296,8 @@ void ws_ds18x20::update() { WS_DEBUG_PRINT("PUBLISHING -> msgDS18x20Response Event Message..."); if (!WS._mqtt->publish(WS._topic_signal_ds18_device, WS._buffer_outgoing, msgSz, 1)) { + WS_DEBUG_PRINTLN("ERROR: Unable to publish DS18x20 event message - " + "MQTT Publish failed!"); return; }; WS_DEBUG_PRINTLN("PUBLISHED!"); diff --git a/src/components/i2c/WipperSnapper_I2C.cpp b/src/components/i2c/WipperSnapper_I2C.cpp index a89fc182b..089d1e496 100644 --- a/src/components/i2c/WipperSnapper_I2C.cpp +++ b/src/components/i2c/WipperSnapper_I2C.cpp @@ -859,8 +859,8 @@ bool WipperSnapper_Component_I2C::encodePublishI2CDeviceEventMsg( memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_I2CResponse_fields, - msgi2cResponse)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_I2CResponse_fields, + msgi2cResponse)) { WS_DEBUG_PRINTLN( "ERROR: Unable to encode I2C device event response message!"); return false; @@ -873,6 +873,7 @@ bool WipperSnapper_Component_I2C::encodePublishI2CDeviceEventMsg( WS_DEBUG_PRINT("PUBLISHING -> I2C Device Sensor Event Message..."); if (!WS._mqtt->publish(WS._topic_signal_i2c_device, WS._buffer_outgoing, msgSz, 1)) { + WS_DEBUG_PRINTLN("ERROR: MQTT Publish failed!"); return false; }; WS_DEBUG_PRINTLN("PUBLISHED!"); diff --git a/src/components/pixels/ws_pixels.cpp b/src/components/pixels/ws_pixels.cpp index 73db519af..a35eb6e42 100644 --- a/src/components/pixels/ws_pixels.cpp +++ b/src/components/pixels/ws_pixels.cpp @@ -170,8 +170,8 @@ void ws_pixels::publishAddStrandResponse(bool is_success, memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_PixelsResponse_fields, - &msgInitResp)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_PixelsResponse_fields, + &msgInitResp)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode " "wippersnapper_signal_v1_PixelsResponse message!"); return; diff --git a/src/components/register/Wippersnapper_Register.cpp b/src/components/register/Wippersnapper_Register.cpp index a7eccae34..522818997 100644 --- a/src/components/register/Wippersnapper_Register.cpp +++ b/src/components/register/Wippersnapper_Register.cpp @@ -46,7 +46,7 @@ bool Wippersnapper::encodePubRegistrationReq() { pb_ostream_t _msg_stream = pb_ostream_from_buffer(_message_buffer, sizeof(_message_buffer)); - _status = pb_encode( + _status = ws_pb_encode( &_msg_stream, wippersnapper_description_v1_CreateDescriptionRequest_fields, &_message); size_t _message_len = _msg_stream.bytes_written; @@ -107,9 +107,10 @@ void Wippersnapper::decodeRegistrationResp(char *data, uint16_t len) { // create input stream for buffer pb_istream_t stream = pb_istream_from_buffer(buffer, len); // decode the stream - if (!pb_decode(&stream, - wippersnapper_description_v1_CreateDescriptionResponse_fields, - &message)) { + if (!ws_pb_decode( + &stream, + wippersnapper_description_v1_CreateDescriptionResponse_fields, + &message)) { WS.haltError("Could not decode registration response"); } // Decode registration response message @@ -140,7 +141,7 @@ void Wippersnapper::decodeRegistrationResp(char *data, uint16_t len) { pb_ostream_t _msg_stream = pb_ostream_from_buffer(_message_buffer, sizeof(_message_buffer)); - bool _status = pb_encode( + bool _status = ws_pb_encode( &_msg_stream, wippersnapper_description_v1_RegistrationComplete_fields, &msg); size_t _message_len = _msg_stream.bytes_written; diff --git a/src/components/uart/drivers/ws_uart_drv_pm25aqi.h b/src/components/uart/drivers/ws_uart_drv_pm25aqi.h index c38772ca9..4eb4c0c92 100644 --- a/src/components/uart/drivers/ws_uart_drv_pm25aqi.h +++ b/src/components/uart/drivers/ws_uart_drv_pm25aqi.h @@ -185,8 +185,8 @@ class ws_uart_drv_pm25aqi : public ws_uart_drv { uint8_t mqttBuffer[512] = {0}; pb_ostream_t ostream = pb_ostream_from_buffer(mqttBuffer, sizeof(mqttBuffer)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_UARTResponse_fields, - &msgUARTResponse)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_UARTResponse_fields, + &msgUARTResponse)) { Serial.println("[ERROR, UART]: Unable to encode device response!"); return; } diff --git a/src/nanopb/ws_pb_helpers.cpp b/src/nanopb/ws_pb_helpers.cpp new file mode 100644 index 000000000..a3b2b8ef1 --- /dev/null +++ b/src/nanopb/ws_pb_helpers.cpp @@ -0,0 +1,61 @@ +/*! + * @file ws_pb_helpers.cpp + * + * Protobuf encode/decode helpers with error logging for Wippersnapper. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Tyeth Gundry 2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#include "ws_pb_helpers.h" +#include "../Wippersnapper.h" + +// ***************************************************************************** +/*! + @brief Decodes a protobuf message from a stream and prints any error. + @param stream + The stream to decode from. + @param fields + The protobuf message fields. + @param dest_struct + The destination struct to decode into. + @return True if decode was successful, false otherwise. +!*/ +// ***************************************************************************** +bool ws_pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, + void *dest_struct) { + bool status = pb_decode(stream, fields, dest_struct); + if (!status) { + WS_DEBUG_PRINT("Protobuf decode error: "); + WS_DEBUG_PRINTLN(PB_GET_ERROR(stream)); + } + return status; +} + +// ***************************************************************************** +/*! + @brief Encodes a protobuf message to a stream and prints any error. + @param stream + The stream to encode to. + @param fields + The protobuf message fields. + @param src_struct + The source struct to encode from. + @return True if encode was successful, false otherwise. +!*/ +// ***************************************************************************** +bool ws_pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, + const void *src_struct) { + bool status = pb_encode(stream, fields, src_struct); + if (!status) { + WS_DEBUG_PRINT("Protobuf encode error: "); + WS_DEBUG_PRINTLN(PB_GET_ERROR(stream)); + } + return status; +} \ No newline at end of file diff --git a/src/nanopb/ws_pb_helpers.h b/src/nanopb/ws_pb_helpers.h new file mode 100644 index 000000000..78a0d5c10 --- /dev/null +++ b/src/nanopb/ws_pb_helpers.h @@ -0,0 +1,28 @@ +/*! + * @file ws_pb_helpers.h + * + * Protobuf encode/decode helpers with error logging for Wippersnapper. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Tyeth Gundry 2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_PB_ENCODE_H +#define WS_PB_ENCODE_H + +#include "pb.h" +#include "pb_decode.h" +#include "pb_encode.h" + +bool ws_pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, + void *dest_struct); + +bool ws_pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, + const void *src_struct); + +#endif // WS_PB_ENCODE_H \ No newline at end of file diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index ce39590b9..4c564b4e1 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -28,7 +28,9 @@ #include "Wippersnapper.h" #define NINAFWVER \ - "1.6.0" /*!< min. nina-fw version compatible with this library. */ + "1.7.7" /*!< min. nina-fw version compatible with this library. */ +#define AIRLIFT_CONNECT_TIMEOUT_MS 20000 /*!< Connection timeout (in ms) */ +#define AIRLIFT_CONNECT_RETRY_DELAY_MS 200 /*!< delay time between retries. */ #define SPIWIFI SPI /*!< Instance of SPI interface used by an AirLift. */ @@ -47,10 +49,14 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { */ /**************************************************************************/ Wippersnapper_AIRLIFT() : Wippersnapper() { - _ssPin = 10; - _ackPin = 7; - _rstPin = 5; + _ssPin = SPIWIFI_SS; // 10; + _ackPin = SPIWIFI_ACK; // 7; + _rstPin = SPIWIFI_RESET; // 5; // should be 7 on PyPortals +#ifdef ESP32_GPIO0 + _gpio0Pin = ESP32_GPIO0; +#else _gpio0Pin = -1; +#endif _wifi = &SPIWIFI; _ssid = 0; _pass = 0; @@ -115,8 +121,11 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { // Was the network within secrets.json found? for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i)) == 0) + if (strcmp(_ssid, WiFi.SSID(i)) == 0) { + WS_DEBUG_PRINT("SSID found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; + } } // User-set network not found, print scan results to serial console @@ -174,9 +183,43 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { /********************************************************/ bool firmwareCheck() { _fv = WiFi.firmwareVersion(); - if (_fv < NINAFWVER) + return compareVersions(_fv, NINAFWVER); + } + + /********************************************************/ + /*! + @brief Compares two version strings. + @param currentVersion + Current version string. + @param requiredVersion + Required version string. + @returns True if the current version is greater than or + equal to the required version, False otherwise. + */ + /********************************************************/ + bool compareVersions(const char *currentVersion, + const char *requiredVersion) { + int curMajor = 0, curMinor = 0, curPatch = 0; + int reqMajor = 0, reqMinor = 0, reqPatch = 0; + + if (!sscanf(currentVersion, "%d.%d.%d", &curMajor, &curMinor, &curPatch) || + !sscanf(requiredVersion, "%d.%d.%d", &reqMajor, &reqMinor, &reqPatch)) { + WS_DEBUG_PRINTLN("Error parsing firmware version strings"); + WS_PRINTER.flush(); + WS_DEBUG_PRINT("Required version: "); + WS_DEBUG_PRINTLN(requiredVersion); + WS_PRINTER.flush(); + WS_DEBUG_PRINT("Current version: "); + WS_DEBUG_PRINTLN(currentVersion); + WS_PRINTER.flush(); return false; - return true; + } + + if (curMajor != reqMajor) + return curMajor > reqMajor; + if (curMinor != reqMinor) + return curMinor > reqMinor; + return curPatch >= reqPatch; } /********************************************************/ @@ -191,6 +234,14 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + @return int32_t RSSI value + */ + /********************************************************/ + int32_t getRSSI() { return WiFi.RSSI(); } + /********************************************************/ /*! @brief Initializes the MQTT client. @@ -234,7 +285,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { protected: const char *_ssid; /*!< Network SSID. */ const char *_pass; /*!< Network password. */ - String _fv; /*!< nina-fw firmware version. */ + const char *_fv = "0.0.1"; /*!< nina-fw firmware version. (placeholder) */ int _ssPin = -1; /*!< SPI S.S. pin. */ int _ackPin = -1; /*!< SPI ACK pin. */ int _rstPin = -1; /*!< SPI RST pin. */ @@ -249,25 +300,86 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { /**************************************************************************/ void _connect() { if (strlen(_ssid) == 0) { - _status = WS_SSID_INVALID; + _status = WS_SSID_INVALID; // possibly unneccesary as already checking + // elsewhere } else { + // disconnect from possible previous connection + _disconnect(); + delay(100); + WiFi.end(); + _wifi->end(); + delay(100); + _wifi->begin(); + feedWDT(); + // reset the esp32 if possible + resetAirLift(); + feedWDT(); - // validate co-processor is physically connected connection - if (WiFi.status() == WL_NO_MODULE) { - WS_DEBUG_PRINT("No ESP32 module detected!"); - return; - } + WS_DEBUG_PRINT("ESP32 booted, version: "); + WS_PRINTER.flush(); + WS_DEBUG_PRINTLN(WiFi.firmwareVersion()); + WS_PRINTER.flush(); + feedWDT(); // validate co-processor's firmware version - if (!firmwareCheck()) + if (!firmwareCheck()) { WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the " "latest version."); + } - // disconnect from possible previous connection - _disconnect(); - + WS_DEBUG_PRINT("Connecting to "); + WS_DEBUG_PRINTLN(_ssid); + WS_PRINTER.flush(); + feedWDT(); WiFi.begin(_ssid, _pass); _status = WS_NET_DISCONNECTED; + + // Use the macro to retry the status check until connected / timed out + int lastResult = -1; + RETRY_FUNCTION_UNTIL_TIMEOUT( + []() -> int { return WiFi.status(); }, // Function call each cycle + int, // return type + lastResult, // return variable + [](int status) { return status == WL_CONNECTED; }, // check + AIRLIFT_CONNECT_TIMEOUT_MS, // timeout interval (ms) + AIRLIFT_CONNECT_RETRY_DELAY_MS); // interval between retries + + if (lastResult == WL_CONNECTED) { + _status = WS_NET_CONNECTED; + // wait 2seconds for connection to stabilize + WS_DELAY_WITH_WDT(2000); + } else { + _status = WS_NET_DISCONNECTED; // maybe connect failed instead? + } + } + } + + /**************************************************************************/ + /*! + @brief Resets the ESP32 module. + */ + /**************************************************************************/ + void resetAirLift() { + if (_rstPin != -1) { + WS_DEBUG_PRINTLN("Resetting ESP32..."); + WS_PRINTER.flush(); + // Chip select for esp32 + pinMode(_ssPin, OUTPUT); + digitalWrite(_ssPin, HIGH); // Do we need to set SS low again? + if (_gpio0Pin != -1) { + pinMode(_gpio0Pin, OUTPUT); + digitalWrite(_gpio0Pin, LOW); + } + pinMode(_rstPin, OUTPUT); + digitalWrite(_rstPin, LOW); + delay(50); + digitalWrite(_rstPin, HIGH); + delay(10); + if (_gpio0Pin != -1) { + pinMode(_gpio0Pin, INPUT); + } + // wait for the ESP32 to boot + delay(2000); } } diff --git a/src/network_interfaces/Wippersnapper_ESP32.h b/src/network_interfaces/Wippersnapper_ESP32.h index 388dc6f76..c95f65864 100644 --- a/src/network_interfaces/Wippersnapper_ESP32.h +++ b/src/network_interfaces/Wippersnapper_ESP32.h @@ -8,7 +8,7 @@ * please support Adafruit and open-source hardware by purchasing * products from Adafruit! * - * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries. + * Copyright (c) Brent Rubell 2020-2024 for Adafruit Industries. * * MIT license, all text here must be included in any redistribution. * @@ -25,7 +25,8 @@ #include "Arduino.h" #include "WiFi.h" #include "WiFiMulti.h" -#include +#include +#include extern Wippersnapper WS; /****************************************************************************/ @@ -44,7 +45,6 @@ class Wippersnapper_ESP32 : public Wippersnapper { Wippersnapper_ESP32() : Wippersnapper() { _ssid = 0; _pass = 0; - _mqtt_client = new WiFiClientSecure; } /**************************************************************************/ @@ -53,8 +53,10 @@ class Wippersnapper_ESP32 : public Wippersnapper { */ /**************************************************************************/ ~Wippersnapper_ESP32() { - if (_mqtt_client) - delete _mqtt_client; + if (_mqtt_client_secure) + delete _mqtt_client_secure; + if (_mqtt_client_insecure) + delete _mqtt_client_insecure; } /********************************************************/ @@ -111,12 +113,20 @@ class Wippersnapper_ESP32 : public Wippersnapper { // Was the network within secrets.json found? for (int i = 0; i < n; ++i) { if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(_ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; } if (WS._isWiFiMulti) { // multi network mode for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) { if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(WS._multiNetworks[j].ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; } } @@ -148,6 +158,14 @@ class Wippersnapper_ESP32 : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + @return int32_t RSSI value + */ + /********************************************************/ + int32_t getRSSI() { return WiFi.RSSI(); } + /********************************************************/ /*! @brief Initializes the MQTT client @@ -156,18 +174,24 @@ class Wippersnapper_ESP32 : public Wippersnapper { */ /********************************************************/ void setupMQTTClient(const char *clientID) { - if (strcmp(WS._config.aio_url, "io.adafruit.com") == 0) { - _mqtt_client->setCACert(_aio_root_ca_prod); - } else if (strcmp(WS._config.aio_url, "io.adafruit.us") == 0) { - _mqtt_client->setCACert(_aio_root_ca_staging); + if (strcmp(WS._config.aio_url, "io.adafruit.com") == 0 || + strcmp(WS._config.aio_url, "io.adafruit.us") == 0) { + _mqtt_client_secure = new NetworkClientSecure(); + _mqtt_client_secure->setCACert( + strcmp(WS._config.aio_url, "io.adafruit.com") == 0 + ? _aio_root_ca_prod + : _aio_root_ca_staging); + WS._mqtt = new Adafruit_MQTT_Client( + _mqtt_client_secure, WS._config.aio_url, WS._config.io_port, clientID, + WS._config.aio_user, WS._config.aio_key); } else { - _mqtt_client->setInsecure(); + // Insecure connections require a NetworkClient object rather than a + // NetworkClientSecure object + _mqtt_client_insecure = new NetworkClient(); + WS._mqtt = new Adafruit_MQTT_Client( + _mqtt_client_insecure, WS._config.aio_url, WS._config.io_port, + clientID, WS._config.aio_user, WS._config.aio_key); } - - // Construct MQTT client - WS._mqtt = new Adafruit_MQTT_Client( - _mqtt_client, WS._config.aio_url, WS._config.io_port, clientID, - WS._config.aio_user, WS._config.aio_key); } /********************************************************/ @@ -198,37 +222,40 @@ class Wippersnapper_ESP32 : public Wippersnapper { const char *connectionType() { return "ESP32"; } protected: - const char *_ssid; ///< WiFi SSID - const char *_pass; ///< WiFi password - WiFiClientSecure *_mqtt_client; ///< Pointer to a WiFi client object (TLS/SSL) - WiFiMulti _wifiMulti; ///< WiFiMulti object for multi-network mode + const char *_ssid; ///< WiFi SSID + const char *_pass; ///< WiFi password + NetworkClientSecure + *_mqtt_client_secure; ///< Pointer to a secure network client object + NetworkClient + *_mqtt_client_insecure; ///< Pointer to an insecure network client object + WiFiMulti _wifiMulti; ///< WiFiMulti object for multi-network mode const char *_aio_root_ca_staging = "-----BEGIN CERTIFICATE-----\n" - "MIIEZTCCA02gAwIBAgIQQAF1BIMUpMghjISpDBbN3zANBgkqhkiG9w0BAQsFADA/\n" - "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" - "DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0MFoXDTIxMDkyOTE5MjE0MFow\n" - "MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT\n" - "AlIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwIVKMz2oJTTDxLs\n" - "jVWSw/iC8ZmmekKIp10mqrUrucVMsa+Oa/l1yKPXD0eUFFU1V4yeqKI5GfWCPEKp\n" - "Tm71O8Mu243AsFzzWTjn7c9p8FoLG77AlCQlh/o3cbMT5xys4Zvv2+Q7RVJFlqnB\n" - "U840yFLuta7tj95gcOKlVKu2bQ6XpUA0ayvTvGbrZjR8+muLj1cpmfgwF126cm/7\n" - "gcWt0oZYPRfH5wm78Sv3htzB2nFd1EbjzK0lwYi8YGd1ZrPxGPeiXOZT/zqItkel\n" - "/xMY6pgJdz+dU/nPAeX1pnAXFK9jpP+Zs5Od3FOnBv5IhR2haa4ldbsTzFID9e1R\n" - "oYvbFQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E\n" - "BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p\n" - "ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE\n" - "p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE\n" - "AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu\n" - "Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0\n" - "LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYf\n" - "r52LFMLGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B\n" - "AQsFAAOCAQEA2UzgyfWEiDcx27sT4rP8i2tiEmxYt0l+PAK3qB8oYevO4C5z70kH\n" - "ejWEHx2taPDY/laBL21/WKZuNTYQHHPD5b1tXgHXbnL7KqC401dk5VvCadTQsvd8\n" - "S8MXjohyc9z9/G2948kLjmE6Flh9dDYrVYA9x2O+hEPGOaEOa1eePynBgPayvUfL\n" - "qjBstzLhWVQLGAkXXmNs+5ZnPBxzDJOLxhF2JIbeQAcH5H0tZrUlo5ZYyOqA7s9p\n" - "O5b85o3AM/OJ+CktFBQtfvBhcJVd9wvlwPsk+uyOy2HI7mNxKKgsBTt375teA2Tw\n" - "UdHkhVNcsAKX1H7GNNLOEADksd86wuoXvg==\n" + "MIIEVzCCAj+gAwIBAgIRALBXPpFzlydw27SHyzpFKzgwDQYJKoZIhvcNAQELBQAw\n" + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw\n" + "WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n" + "RW5jcnlwdDELMAkGA1UEAxMCRTYwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATZ8Z5G\n" + "h/ghcWCoJuuj+rnq2h25EqfUJtlRFLFhfHWWvyILOR/VvtEKRqotPEoJhC6+QJVV\n" + "6RlAN2Z17TJOdwRJ+HB7wxjnzvdxEP6sdNgA1O1tHHMWMxCcOrLqbGL0vbijgfgw\n" + "gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\n" + "ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSTJ0aYA6lRaI6Y1sRCSNsj\n" + "v1iU0jAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB\n" + "AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g\n" + "BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu\n" + "Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAfYt7SiA1sgWGCIpunk46r4AExIRc\n" + "MxkKgUhNlrrv1B21hOaXN/5miE+LOTbrcmU/M9yvC6MVY730GNFoL8IhJ8j8vrOL\n" + "pMY22OP6baS1k9YMrtDTlwJHoGby04ThTUeBDksS9RiuHvicZqBedQdIF65pZuhp\n" + "eDcGBcLiYasQr/EO5gxxtLyTmgsHSOVSBcFOn9lgv7LECPq9i7mfH3mpxgrRKSxH\n" + "pOoZ0KXMcB+hHuvlklHntvcI0mMMQ0mhYj6qtMFStkF1RpCG3IPdIwpVCQqu8GV7\n" + "s8ubknRzs+3C/Bm19RFOoiPpDkwvyNfvmQ14XkyqqKK5oZ8zhD32kFRQkxa8uZSu\n" + "h4aTImFxknu39waBxIRXE4jKxlAmQc4QjFZoq1KmQqQg0J/1JF8RlFvJas1VcjLv\n" + "YlvUB2t6npO6oQjB3l+PNf0DpQH7iUx3Wz5AjQCi6L25FjyE06q6BZ/QlmtYdl/8\n" + "ZYao4SRqPEs/6cAiF+Qf5zg2UkaWtDphl1LKMuTNLotvsX99HP69V2faNyegodQ0\n" + "LyTApr/vT01YPE46vNsDLgK+4cL6TrzC/a4WcmF5SRJ938zrv/duJHLXQIku5v0+\n" + "EwOy59Hdm0PT/Er/84dDV0CSjdR/2XuZM3kpysSKLgD1cKiDA+IRguODCxfO9cyY\n" + "Ig46v9mFmBvyH04=\n" "-----END CERTIFICATE-----\n"; ///< Root certificate for io.adafruit.us const char *_aio_root_ca_prod = diff --git a/src/network_interfaces/Wippersnapper_ESP8266.h b/src/network_interfaces/Wippersnapper_ESP8266.h index 35a95db0d..c7fa6b693 100644 --- a/src/network_interfaces/Wippersnapper_ESP8266.h +++ b/src/network_interfaces/Wippersnapper_ESP8266.h @@ -136,13 +136,22 @@ class Wippersnapper_ESP8266 : public Wippersnapper { // Was the network within secrets.json found? for (int i = 0; i < n; ++i) { if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(_ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; } if (WS._isWiFiMulti) { // multi network mode for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) { - if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0) + if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(WS._multiNetworks[j].ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; + } } } } @@ -172,6 +181,14 @@ class Wippersnapper_ESP8266 : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + @return int32_t RSSI value + */ + /********************************************************/ + int32_t getRSSI() { return WiFi.RSSI(); } + /*******************************************************************/ /*! @brief Sets up an Adafruit_MQTT_Client @@ -247,7 +264,7 @@ class Wippersnapper_ESP8266 : public Wippersnapper { _status = WS_NET_DISCONNECTED; delay(100); - if (strlen(WS._multiNetworks[0].ssid) > 0) { + if (WS._isWiFiMulti) { // multi network mode for (int i = 0; i < WS_MAX_ALT_WIFI_NETWORKS; i++) { if (strlen(WS._multiNetworks[i].ssid) > 0 && diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h index aa56c3b40..74f50bffe 100644 --- a/src/network_interfaces/Wippersnapper_WIFININA.h +++ b/src/network_interfaces/Wippersnapper_WIFININA.h @@ -130,8 +130,11 @@ class Wippersnapper_WIFININA : public Wippersnapper { // Was the network within secrets.json found? for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i)) == 0) + if (strcmp(_ssid, WiFi.SSID(i)) == 0) { + WS_DEBUG_PRINT("SSID found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; + } } // User-set network not found, print scan results to serial console @@ -185,6 +188,14 @@ class Wippersnapper_WIFININA : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + @return int32_t RSSI value + */ + /********************************************************/ + int32_t getRSSI() { return WiFi.RSSI(); } + /********************************************************/ /*! @brief Initializes the MQTT client. diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index 55578fb74..283a24467 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -18,11 +18,16 @@ #define WS_NETWORKING_PICO_H #ifdef ARDUINO_ARCH_RP2040 + +#define PICO_CONNECT_TIMEOUT_MS 20000 /*!< Connection timeout (in ms) */ +#define PICO_CONNECT_RETRY_DELAY_MS 200 /*!< delay time between retries. */ + #include "Wippersnapper.h" #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #include "Arduino.h" +#include #include extern Wippersnapper WS; @@ -42,7 +47,6 @@ class ws_networking_pico : public Wippersnapper { ws_networking_pico() : Wippersnapper() { _ssid = 0; _pass = 0; - _mqtt_client = new WiFiClientSecure; } /**************************************************************************/ @@ -51,8 +55,10 @@ class ws_networking_pico : public Wippersnapper { */ /**************************************************************************/ ~ws_networking_pico() { - if (_mqtt_client) - delete _mqtt_client; + if (_mqtt_client_secure) + delete _mqtt_client_secure; + if (_mqtt_client_secure) + delete _mqtt_client_secure; } /********************************************************/ @@ -109,12 +115,20 @@ class ws_networking_pico : public Wippersnapper { // Was the network within secrets.json found? for (int i = 0; i < n; ++i) { if (strcmp(_ssid, WiFi.SSID(i)) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(_ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; } if (WS._isWiFiMulti) { // multi network mode for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) { if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i)) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(WS._multiNetworks[j].ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; } } @@ -146,6 +160,14 @@ class ws_networking_pico : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + @return int32_t RSSI value + */ + /********************************************************/ + int32_t getRSSI() { return WiFi.RSSI(); } + /********************************************************/ /*! @brief Initializes the MQTT client @@ -154,19 +176,22 @@ class ws_networking_pico : public Wippersnapper { */ /********************************************************/ void setupMQTTClient(const char *clientID) { - // Set CA cert depending on the server we're connecting to - // compare WS._config.aio_url to "io.adafruit.com" - if (strcmp(WS._config.aio_url, "io.adafruit.com") == 0) { - _mqtt_client->setCACert(_aio_root_ca_prod); - } else if (strcmp(WS._config.aio_url, "io.adafruit.us") == 0) { - _mqtt_client->setCACert(_aio_root_ca_staging); + if (strcmp(WS._config.aio_url, "io.adafruit.com") == 0 || + strcmp(WS._config.aio_url, "io.adafruit.us") == 0) { + _mqtt_client_secure = new WiFiClientSecure(); + _mqtt_client_secure->setCACert( + strcmp(WS._config.aio_url, "io.adafruit.com") == 0 + ? _aio_root_ca_prod + : _aio_root_ca_staging); + WS._mqtt = new Adafruit_MQTT_Client( + _mqtt_client_secure, WS._config.aio_url, WS._config.io_port, clientID, + WS._config.aio_user, WS._config.aio_key); } else { - _mqtt_client->setInsecure(); + _mqtt_client_insecure = new WiFiClient(); + WS._mqtt = new Adafruit_MQTT_Client( + _mqtt_client_insecure, WS._config.aio_url, WS._config.io_port, + clientID, WS._config.aio_user, WS._config.aio_key); } - - WS._mqtt = new Adafruit_MQTT_Client( - _mqtt_client, WS._config.aio_url, WS._config.io_port, clientID, - WS._config.aio_user, WS._config.aio_key); } /********************************************************/ @@ -197,37 +222,40 @@ class ws_networking_pico : public Wippersnapper { const char *connectionType() { return "Pico"; } protected: - const char *_ssid; ///< WiFi SSID - const char *_pass; ///< WiFi password - WiFiClientSecure *_mqtt_client; ///< Pointer to a secure MQTT client object - WiFiMulti _wifiMulti; ///< WiFiMulti object for multi-network mode + const char *_ssid; ///< WiFi SSID + const char *_pass; ///< WiFi password + WiFiClient + *_mqtt_client_insecure; ///< Pointer to an insecure WiFi client object + WiFiClientSecure + *_mqtt_client_secure; ///< Pointer to a secure WiFi client object + WiFiMulti _wifiMulti; ///< WiFiMulti object for multi-network mode const char *_aio_root_ca_staging = "-----BEGIN CERTIFICATE-----\n" - "MIIEZTCCA02gAwIBAgIQQAF1BIMUpMghjISpDBbN3zANBgkqhkiG9w0BAQsFADA/\n" - "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" - "DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0MFoXDTIxMDkyOTE5MjE0MFow\n" - "MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT\n" - "AlIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwIVKMz2oJTTDxLs\n" - "jVWSw/iC8ZmmekKIp10mqrUrucVMsa+Oa/l1yKPXD0eUFFU1V4yeqKI5GfWCPEKp\n" - "Tm71O8Mu243AsFzzWTjn7c9p8FoLG77AlCQlh/o3cbMT5xys4Zvv2+Q7RVJFlqnB\n" - "U840yFLuta7tj95gcOKlVKu2bQ6XpUA0ayvTvGbrZjR8+muLj1cpmfgwF126cm/7\n" - "gcWt0oZYPRfH5wm78Sv3htzB2nFd1EbjzK0lwYi8YGd1ZrPxGPeiXOZT/zqItkel\n" - "/xMY6pgJdz+dU/nPAeX1pnAXFK9jpP+Zs5Od3FOnBv5IhR2haa4ldbsTzFID9e1R\n" - "oYvbFQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E\n" - "BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p\n" - "ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE\n" - "p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE\n" - "AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu\n" - "Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0\n" - "LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYf\n" - "r52LFMLGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B\n" - "AQsFAAOCAQEA2UzgyfWEiDcx27sT4rP8i2tiEmxYt0l+PAK3qB8oYevO4C5z70kH\n" - "ejWEHx2taPDY/laBL21/WKZuNTYQHHPD5b1tXgHXbnL7KqC401dk5VvCadTQsvd8\n" - "S8MXjohyc9z9/G2948kLjmE6Flh9dDYrVYA9x2O+hEPGOaEOa1eePynBgPayvUfL\n" - "qjBstzLhWVQLGAkXXmNs+5ZnPBxzDJOLxhF2JIbeQAcH5H0tZrUlo5ZYyOqA7s9p\n" - "O5b85o3AM/OJ+CktFBQtfvBhcJVd9wvlwPsk+uyOy2HI7mNxKKgsBTt375teA2Tw\n" - "UdHkhVNcsAKX1H7GNNLOEADksd86wuoXvg==\n" + "MIIEVzCCAj+gAwIBAgIRALBXPpFzlydw27SHyzpFKzgwDQYJKoZIhvcNAQELBQAw\n" + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw\n" + "WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n" + "RW5jcnlwdDELMAkGA1UEAxMCRTYwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATZ8Z5G\n" + "h/ghcWCoJuuj+rnq2h25EqfUJtlRFLFhfHWWvyILOR/VvtEKRqotPEoJhC6+QJVV\n" + "6RlAN2Z17TJOdwRJ+HB7wxjnzvdxEP6sdNgA1O1tHHMWMxCcOrLqbGL0vbijgfgw\n" + "gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\n" + "ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSTJ0aYA6lRaI6Y1sRCSNsj\n" + "v1iU0jAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB\n" + "AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g\n" + "BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu\n" + "Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAfYt7SiA1sgWGCIpunk46r4AExIRc\n" + "MxkKgUhNlrrv1B21hOaXN/5miE+LOTbrcmU/M9yvC6MVY730GNFoL8IhJ8j8vrOL\n" + "pMY22OP6baS1k9YMrtDTlwJHoGby04ThTUeBDksS9RiuHvicZqBedQdIF65pZuhp\n" + "eDcGBcLiYasQr/EO5gxxtLyTmgsHSOVSBcFOn9lgv7LECPq9i7mfH3mpxgrRKSxH\n" + "pOoZ0KXMcB+hHuvlklHntvcI0mMMQ0mhYj6qtMFStkF1RpCG3IPdIwpVCQqu8GV7\n" + "s8ubknRzs+3C/Bm19RFOoiPpDkwvyNfvmQ14XkyqqKK5oZ8zhD32kFRQkxa8uZSu\n" + "h4aTImFxknu39waBxIRXE4jKxlAmQc4QjFZoq1KmQqQg0J/1JF8RlFvJas1VcjLv\n" + "YlvUB2t6npO6oQjB3l+PNf0DpQH7iUx3Wz5AjQCi6L25FjyE06q6BZ/QlmtYdl/8\n" + "ZYao4SRqPEs/6cAiF+Qf5zg2UkaWtDphl1LKMuTNLotvsX99HP69V2faNyegodQ0\n" + "LyTApr/vT01YPE46vNsDLgK+4cL6TrzC/a4WcmF5SRJ938zrv/duJHLXQIku5v0+\n" + "EwOy59Hdm0PT/Er/84dDV0CSjdR/2XuZM3kpysSKLgD1cKiDA+IRguODCxfO9cyY\n" + "Ig46v9mFmBvyH04=\n" "-----END CERTIFICATE-----\n"; ///< Root certificate for io.adafruit.us const char *_aio_root_ca_prod = @@ -299,16 +327,22 @@ class ws_networking_pico : public Wippersnapper { WS.feedWDT(); } else { WiFi.begin(_ssid, _pass); - // Wait setTimeout duration for a connection and check if connected - // every 5 seconds - for (int i = 0; i < 4; i++) { - WS.feedWDT(); - delay(5000); - WS.feedWDT(); - if (WiFi.status() == WL_CONNECTED) { - _status = WS_NET_CONNECTED; - return; - } + + // Use the macro to retry the status check until connected / timed out + int lastResult; + RETRY_FUNCTION_UNTIL_TIMEOUT( + []() -> int { return WiFi.status(); }, // Function call each cycle + int, // return type + lastResult, // return variable (unused here) + [](int status) { return status == WL_CONNECTED; }, // check + PICO_CONNECT_TIMEOUT_MS, // timeout interval (ms) + PICO_CONNECT_RETRY_DELAY_MS); // interval between retries + + if (lastResult == WL_CONNECTED) { + _status = WS_NET_CONNECTED; + // wait 2seconds for connection to stabilize + WS_DELAY_WITH_WDT(2000); + return; } } _status = WS_NET_DISCONNECTED;