From 7b0fdf584d9a945affe002bbfa07638d84159059 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Wed, 8 May 2024 17:49:53 +0100 Subject: [PATCH 01/54] Initial tweak to pb_encode --- src/components/i2c/WipperSnapper_I2C.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/i2c/WipperSnapper_I2C.cpp b/src/components/i2c/WipperSnapper_I2C.cpp index e41f1c323..bbb47a40a 100644 --- a/src/components/i2c/WipperSnapper_I2C.cpp +++ b/src/components/i2c/WipperSnapper_I2C.cpp @@ -852,6 +852,8 @@ bool WipperSnapper_Component_I2C::encodePublishI2CDeviceEventMsg( msgi2cResponse)) { WS_DEBUG_PRINTLN( "ERROR: Unable to encode I2C device event response message!"); + WS_DEBUG_PRINT("Protobuf Encode Error: "); + WS_DEBUG_PRINTLN(PB_GET_ERROR(&ostream)); return false; } From d8f99f929744c224e5f081d332d3eec8844c2442 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Wed, 8 May 2024 17:50:52 +0100 Subject: [PATCH 02/54] Swap out pb_encode for i2c --- src/Wippersnapper.h | 3 +++ src/components/i2c/WipperSnapper_I2C.cpp | 4 +--- src/nanopb/ws_pb_encode.h | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 src/nanopb/ws_pb_encode.h diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index cd93d961a..2ea3999bc 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -59,6 +59,9 @@ {} ///< Prints line from debug output. #endif +// Wippersnapper pb helpers +#include + // Wippersnapper components #include "components/analogIO/Wippersnapper_AnalogIO.h" #include "components/digitalIO/Wippersnapper_DigitalGPIO.h" diff --git a/src/components/i2c/WipperSnapper_I2C.cpp b/src/components/i2c/WipperSnapper_I2C.cpp index bbb47a40a..9ab51ab25 100644 --- a/src/components/i2c/WipperSnapper_I2C.cpp +++ b/src/components/i2c/WipperSnapper_I2C.cpp @@ -848,12 +848,10 @@ 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, + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_I2CResponse_fields, msgi2cResponse)) { WS_DEBUG_PRINTLN( "ERROR: Unable to encode I2C device event response message!"); - WS_DEBUG_PRINT("Protobuf Encode Error: "); - WS_DEBUG_PRINTLN(PB_GET_ERROR(&ostream)); return false; } diff --git a/src/nanopb/ws_pb_encode.h b/src/nanopb/ws_pb_encode.h new file mode 100644 index 000000000..0aebcf802 --- /dev/null +++ b/src/nanopb/ws_pb_encode.h @@ -0,0 +1,17 @@ +#ifndef WS_PB_ENCODE_H +#define WS_PB_ENCODE_H + +#include "pb.h" +#include "pb_encode.h" +#include + +static 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; +} + +#endif // WS_PB_ENCODE_H \ No newline at end of file From f00e41e262a05d490eef826256d45a6e8e26fad0 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Thu, 9 May 2024 16:13:50 +0100 Subject: [PATCH 03/54] WIP: decode added all untested --- platformio.ini | 37 +++++++++-- src/Wippersnapper.cpp | 62 +++++++++---------- src/Wippersnapper.h | 2 +- .../register/Wippersnapper_Register.cpp | 2 +- src/nanopb/ws_pb_encode.h | 17 ----- src/nanopb/ws_pb_helpers.h | 29 +++++++++ 6 files changed, 94 insertions(+), 55 deletions(-) delete mode 100644 src/nanopb/ws_pb_encode.h create mode 100644 src/nanopb/ws_pb_helpers.h diff --git a/platformio.ini b/platformio.ini index 18c993abf..531ef6a38 100644 --- a/platformio.ini +++ b/platformio.ini @@ -95,6 +95,9 @@ lib_ignore = WiFiNINA, 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 @@ -294,14 +297,38 @@ 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 = 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 d702b9158..781a295fd 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -357,7 +357,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_pin_v1_ConfigurePinRequest_fields, &pinReqMsg)) { WS_DEBUG_PRINTLN("ERROR: Could not decode CreateSignalRequest") is_success = false; @@ -398,7 +398,7 @@ 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,7 +442,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_pin_v1_ConfigurePinRequests_fields, &msg)) { WS_DEBUG_PRINTLN("ERROR: Could not decode CreateSignalRequest") is_success = false; @@ -463,7 +463,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,7 +496,7 @@ 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, + if (!ws_pb_decode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, encodedSignalMsg)) { WS_DEBUG_PRINTLN( "ERROR (decodeSignalMsg):, Could not decode CreateSignalRequest") @@ -609,7 +609,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequest_fields, &msgI2CDeviceInitRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode I2CDeviceInitRequest message."); return false; @@ -682,7 +682,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CBusScanRequest_fields, &msgScanReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_i2c_v1_I2CBusScanRequest"); @@ -735,7 +735,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequests_fields, &msgI2CDeviceInitRequestList)) { WS_DEBUG_PRINTLN("ERROR: Could not decode I2CDeviceInitRequests"); is_success = false; @@ -751,7 +751,7 @@ 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, + 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,7 +797,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceUpdateRequest_fields, &msgI2CDeviceUpdateRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode I2CDeviceUpdateRequest message."); @@ -829,7 +829,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceDeinitRequest_fields, &msgI2CDeviceDeinitRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode I2CDeviceDeinitRequest message."); @@ -891,7 +891,7 @@ 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, + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_I2CRequest_fields, &WS.msgSignalI2C)) WS_DEBUG_PRINTLN("ERROR: Unable to decode I2C message"); } @@ -917,7 +917,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_servo_v1_ServoAttachRequest_fields, &msgServoAttachReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_servo_v1_ServoAttachRequest"); @@ -988,7 +988,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_servo_v1_ServoWriteRequest_fields, &msgServoWriteReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_servo_v1_ServoWriteRequest"); @@ -1018,7 +1018,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_servo_v1_ServoDetachRequest_fields, &msgServoDetachReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_servo_v1_ServoDetachRequest"); @@ -1071,7 +1071,7 @@ 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, + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_ServoRequest_fields, &WS.msgServo)) WS_DEBUG_PRINTLN("ERROR: Unable to decode servo message"); } @@ -1096,7 +1096,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_pwm_v1_PWMAttachRequest_fields, &msgPWMAttachRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_pwm_v1_PWMAttachRequest"); @@ -1160,7 +1160,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_pwm_v1_PWMDetachRequest_fields, &msgPWMDetachRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_pwm_v1_PWMDetachRequest"); @@ -1187,7 +1187,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_pwm_v1_PWMWriteFrequencyRequest_fields, &msgPWMWriteFreqRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode " "wippersnapper_pwm_v1_PWMWriteFrequencyRequest"); @@ -1220,7 +1220,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_pwm_v1_PWMWriteDutyCycleRequest_fields, &msgPWMWriteDutyCycleRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode " "wippersnapper_pwm_v1_PWMWriteDutyCycleRequest"); @@ -1275,7 +1275,7 @@ 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, + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_PWMRequest_fields, &WS.msgPWM)) WS_DEBUG_PRINTLN("ERROR: Unable to decode PWM message"); } @@ -1303,7 +1303,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_ds18x20_v1_Ds18x20InitRequest_fields, &msgDS18xInitReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode " "wippersnapper_ds18x20_v1_Ds18x20InitRequest"); @@ -1319,7 +1319,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_ds18x20_v1_Ds18x20DeInitRequest_fields, &msgDS18xDeInitReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode " "wippersnapper_ds18x20_v1_Ds18x20DeInitRequest"); @@ -1363,7 +1363,7 @@ 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, + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_Ds18x20Request_fields, &WS.msgSignalDS)) WS_DEBUG_PRINTLN("ERROR: Unable to decode DS message"); } @@ -1393,7 +1393,7 @@ 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, + 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!"); @@ -1414,7 +1414,7 @@ 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, + 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!"); @@ -1432,7 +1432,7 @@ 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, + 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!"); @@ -1474,7 +1474,7 @@ 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, + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_PixelsRequest_fields, &WS.msgPixels)) WS_DEBUG_PRINTLN("ERROR: Unable to decode pixel topic message"); } @@ -1503,7 +1503,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_uart_v1_UARTDeviceAttachRequest_fields, &msgUARTInitReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode message of type: UARTDeviceAttachRequest!"); @@ -1553,7 +1553,7 @@ 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, + if (!ws_pb_decode(stream, wippersnapper_uart_v1_UARTDeviceDetachRequest_fields, &msgUARTDetachReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode message!"); return false; @@ -1594,7 +1594,7 @@ 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, + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_UARTRequest_fields, &WS.msgSignalUART)) WS_DEBUG_PRINTLN("ERROR: Unable to decode UART Signal message"); } diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index 2ea3999bc..87fffc62c 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -60,7 +60,7 @@ #endif // Wippersnapper pb helpers -#include +#include // Wippersnapper components #include "components/analogIO/Wippersnapper_AnalogIO.h" diff --git a/src/components/register/Wippersnapper_Register.cpp b/src/components/register/Wippersnapper_Register.cpp index a7eccae34..9891dadff 100644 --- a/src/components/register/Wippersnapper_Register.cpp +++ b/src/components/register/Wippersnapper_Register.cpp @@ -107,7 +107,7 @@ 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, + if (!ws_pb_decode(&stream, wippersnapper_description_v1_CreateDescriptionResponse_fields, &message)) { WS.haltError("Could not decode registration response"); diff --git a/src/nanopb/ws_pb_encode.h b/src/nanopb/ws_pb_encode.h deleted file mode 100644 index 0aebcf802..000000000 --- a/src/nanopb/ws_pb_encode.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef WS_PB_ENCODE_H -#define WS_PB_ENCODE_H - -#include "pb.h" -#include "pb_encode.h" -#include - -static 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; -} - -#endif // WS_PB_ENCODE_H \ 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..b08e769c7 --- /dev/null +++ b/src/nanopb/ws_pb_helpers.h @@ -0,0 +1,29 @@ +#ifndef WS_PB_ENCODE_H +#define WS_PB_ENCODE_H + +#include "pb.h" +#include "pb_decode.h" +#include "pb_encode.h" +#include + +static 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; +} + +static 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; +} + +#endif // WS_PB_ENCODE_H \ No newline at end of file From c89b4c36bebeceb580ef5498ce55cfa93e6d7a1d Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 9 May 2024 16:03:58 +0100 Subject: [PATCH 04/54] CLang and Doxygen --- platformio.ini | 3 +- src/Wippersnapper.cpp | 91 +++++++++++-------- src/components/i2c/WipperSnapper_I2C.cpp | 2 +- .../register/Wippersnapper_Register.cpp | 7 +- src/nanopb/ws_pb_helpers.h | 39 ++++++++ 5 files changed, 97 insertions(+), 45 deletions(-) diff --git a/platformio.ini b/platformio.ini index 531ef6a38..74b32c718 100644 --- a/platformio.ini +++ b/platformio.ini @@ -301,8 +301,9 @@ build_type = debug upload_protocol = sam-ba ; upload_protocol = jlink debug_tool = jlink +monitor_port = auto ; monitor_port = jlink -monitor_port = socket://localhost:19021 +; monitor_port = socket://localhost:19021 ; debug_init_break = tbreak clearConfiguration lib_ignore = USBHost build_flags = -DUSE_TINYUSB diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 781a295fd..948214139 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -358,7 +358,7 @@ bool cbDecodePinConfigMsg(pb_istream_t *stream, const pb_field_t *field, wippersnapper_pin_v1_ConfigurePinRequest pinReqMsg = wippersnapper_pin_v1_ConfigurePinRequest_init_zero; if (!ws_pb_decode(stream, wippersnapper_pin_v1_ConfigurePinRequest_fields, - &pinReqMsg)) { + &pinReqMsg)) { WS_DEBUG_PRINTLN("ERROR: Could not decode CreateSignalRequest") is_success = false; } @@ -398,7 +398,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 (!ws_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; } @@ -443,7 +444,7 @@ bool cbSignalMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { msg.list.arg = field->pData; // decode each ConfigurePinRequest sub-message if (!ws_pb_decode(stream, wippersnapper_pin_v1_ConfigurePinRequests_fields, - &msg)) { + &msg)) { WS_DEBUG_PRINTLN("ERROR: Could not decode CreateSignalRequest") is_success = false; WS.pinCfgCompleted = false; @@ -497,7 +498,7 @@ bool Wippersnapper::decodeSignalMsg( // decode the CreateSignalRequest, calls cbSignalMessage and assoc. callbacks pb_istream_t stream = pb_istream_from_buffer(WS._buffer, WS.bufSize); if (!ws_pb_decode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, - encodedSignalMsg)) { + encodedSignalMsg)) { WS_DEBUG_PRINTLN( "ERROR (decodeSignalMsg):, Could not decode CreateSignalRequest") is_success = false; @@ -610,7 +611,7 @@ bool cbDecodeI2CDeviceInitRequestList(pb_istream_t *stream, wippersnapper_i2c_v1_I2CDeviceInitRequest msgI2CDeviceInitRequest = wippersnapper_i2c_v1_I2CDeviceInitRequest_init_zero; if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequest_fields, - &msgI2CDeviceInitRequest)) { + &msgI2CDeviceInitRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode I2CDeviceInitRequest message."); return false; } @@ -683,7 +684,7 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, wippersnapper_i2c_v1_I2CBusScanRequest msgScanReq = wippersnapper_i2c_v1_I2CBusScanRequest_init_zero; if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CBusScanRequest_fields, - &msgScanReq)) { + &msgScanReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_i2c_v1_I2CBusScanRequest"); return false; // fail out if we can't decode the request @@ -736,7 +737,7 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, msgI2CDeviceInitRequestList.list.arg = field->pData; // Decode each sub-message if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequests_fields, - &msgI2CDeviceInitRequestList)) { + &msgI2CDeviceInitRequestList)) { WS_DEBUG_PRINTLN("ERROR: Could not decode I2CDeviceInitRequests"); is_success = false; } @@ -752,7 +753,7 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, wippersnapper_i2c_v1_I2CDeviceInitRequest_init_zero; // Decode stream into struct, msgI2CDeviceInitRequest if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequest_fields, - &msgI2CDeviceInitRequest)) { + &msgI2CDeviceInitRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode I2CDeviceInitRequest message."); return false; // fail out if we can't decode } @@ -797,8 +798,9 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, wippersnapper_i2c_v1_I2CDeviceUpdateRequest_init_zero; // Decode stream into message - if (!ws_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 +831,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 (!ws_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 @@ -892,7 +895,7 @@ void cbSignalI2CReq(char *data, uint16_t len) { // Decode I2C signal request pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); if (!ws_pb_decode(&istream, wippersnapper_signal_v1_I2CRequest_fields, - &WS.msgSignalI2C)) + &WS.msgSignalI2C)) WS_DEBUG_PRINTLN("ERROR: Unable to decode I2C message"); } @@ -918,7 +921,7 @@ bool cbDecodeServoMsg(pb_istream_t *stream, const pb_field_t *field, wippersnapper_servo_v1_ServoAttachRequest msgServoAttachReq = wippersnapper_servo_v1_ServoAttachRequest_init_zero; if (!ws_pb_decode(stream, wippersnapper_servo_v1_ServoAttachRequest_fields, - &msgServoAttachReq)) { + &msgServoAttachReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_servo_v1_ServoAttachRequest"); #ifdef USE_DISPLAY @@ -989,7 +992,7 @@ bool cbDecodeServoMsg(pb_istream_t *stream, const pb_field_t *field, wippersnapper_servo_v1_ServoWriteRequest_init_zero; if (!ws_pb_decode(stream, wippersnapper_servo_v1_ServoWriteRequest_fields, - &msgServoWriteReq)) { + &msgServoWriteReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_servo_v1_ServoWriteRequest"); return false; // fail out if we can't decode the request @@ -1019,7 +1022,7 @@ bool cbDecodeServoMsg(pb_istream_t *stream, const pb_field_t *field, wippersnapper_servo_v1_ServoDetachRequest msgServoDetachReq = wippersnapper_servo_v1_ServoDetachRequest_init_zero; if (!ws_pb_decode(stream, wippersnapper_servo_v1_ServoDetachRequest_fields, - &msgServoDetachReq)) { + &msgServoDetachReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_servo_v1_ServoDetachRequest"); return false; // fail out if we can't decode the request @@ -1072,7 +1075,7 @@ 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 (!ws_pb_decode(&istream, wippersnapper_signal_v1_ServoRequest_fields, - &WS.msgServo)) + &WS.msgServo)) WS_DEBUG_PRINTLN("ERROR: Unable to decode servo message"); } @@ -1097,7 +1100,7 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { wippersnapper_pwm_v1_PWMAttachRequest msgPWMAttachRequest = wippersnapper_pwm_v1_PWMAttachRequest_init_zero; if (!ws_pb_decode(stream, wippersnapper_pwm_v1_PWMAttachRequest_fields, - &msgPWMAttachRequest)) { + &msgPWMAttachRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_pwm_v1_PWMAttachRequest"); #ifdef USE_DISPLAY @@ -1161,7 +1164,7 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { wippersnapper_pwm_v1_PWMDetachRequest msgPWMDetachRequest = wippersnapper_pwm_v1_PWMDetachRequest_init_zero; if (!ws_pb_decode(stream, wippersnapper_pwm_v1_PWMDetachRequest_fields, - &msgPWMDetachRequest)) { + &msgPWMDetachRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_pwm_v1_PWMDetachRequest"); #ifdef USE_DISPLAY @@ -1187,8 +1190,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 (!ws_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 +1224,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 (!ws_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 @@ -1276,7 +1281,7 @@ 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 (!ws_pb_decode(&istream, wippersnapper_signal_v1_PWMRequest_fields, - &WS.msgPWM)) + &WS.msgPWM)) WS_DEBUG_PRINTLN("ERROR: Unable to decode PWM message"); } @@ -1303,8 +1308,9 @@ bool cbDecodeDs18x20Msg(pb_istream_t *stream, const pb_field_t *field, wippersnapper_ds18x20_v1_Ds18x20InitRequest msgDS18xInitReq = wippersnapper_ds18x20_v1_Ds18x20InitRequest_init_zero; - if (!ws_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 +1325,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 (!ws_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 @@ -1364,7 +1371,7 @@ void cbSignalDSReq(char *data, uint16_t len) { // Decode DS signal request pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); if (!ws_pb_decode(&istream, wippersnapper_signal_v1_Ds18x20Request_fields, - &WS.msgSignalDS)) + &WS.msgSignalDS)) WS_DEBUG_PRINTLN("ERROR: Unable to decode DS message"); } @@ -1393,8 +1400,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 (!ws_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 +1422,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 (!ws_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; @@ -1433,7 +1442,7 @@ bool cbDecodePixelsMsg(pb_istream_t *stream, const pb_field_t *field, wippersnapper_pixels_v1_PixelsWriteRequest msgPixelsWritereq = wippersnapper_pixels_v1_PixelsWriteRequest_init_zero; if (!ws_pb_decode(stream, wippersnapper_pixels_v1_PixelsWriteRequest_fields, - &msgPixelsWritereq)) { + &msgPixelsWritereq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode message of type " "wippersnapper_pixels_v1_PixelsWriteRequest!"); return false; @@ -1475,7 +1484,7 @@ 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 (!ws_pb_decode(&istream, wippersnapper_signal_v1_PixelsRequest_fields, - &WS.msgPixels)) + &WS.msgPixels)) WS_DEBUG_PRINTLN("ERROR: Unable to decode pixel topic message"); } @@ -1503,8 +1512,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 (!ws_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; @@ -1553,8 +1563,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 (!ws_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; } @@ -1595,7 +1606,7 @@ void cbSignalUARTReq(char *data, uint16_t len) { // Decode DS signal request pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); if (!ws_pb_decode(&istream, wippersnapper_signal_v1_UARTRequest_fields, - &WS.msgSignalUART)) + &WS.msgSignalUART)) WS_DEBUG_PRINTLN("ERROR: Unable to decode UART Signal message"); } diff --git a/src/components/i2c/WipperSnapper_I2C.cpp b/src/components/i2c/WipperSnapper_I2C.cpp index 9ab51ab25..927a5b1cd 100644 --- a/src/components/i2c/WipperSnapper_I2C.cpp +++ b/src/components/i2c/WipperSnapper_I2C.cpp @@ -849,7 +849,7 @@ bool WipperSnapper_Component_I2C::encodePublishI2CDeviceEventMsg( pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_I2CResponse_fields, - msgi2cResponse)) { + msgi2cResponse)) { WS_DEBUG_PRINTLN( "ERROR: Unable to encode I2C device event response message!"); return false; diff --git a/src/components/register/Wippersnapper_Register.cpp b/src/components/register/Wippersnapper_Register.cpp index 9891dadff..071927449 100644 --- a/src/components/register/Wippersnapper_Register.cpp +++ b/src/components/register/Wippersnapper_Register.cpp @@ -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 (!ws_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 diff --git a/src/nanopb/ws_pb_helpers.h b/src/nanopb/ws_pb_helpers.h index b08e769c7..79cbf9807 100644 --- a/src/nanopb/ws_pb_helpers.h +++ b/src/nanopb/ws_pb_helpers.h @@ -1,3 +1,17 @@ +/*! + * @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 @@ -6,6 +20,19 @@ #include "pb_encode.h" #include + +// ***************************************************************************** +/*! + @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. +!*/ +// ***************************************************************************** static bool ws_pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) { bool status = pb_decode(stream, fields, dest_struct); @@ -16,6 +43,18 @@ static bool ws_pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, 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. +!*/ +// ***************************************************************************** static 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); From 64ba1fb6ceaab8e2cb2f293dd78bffc16c70dae5 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 9 May 2024 16:50:07 +0100 Subject: [PATCH 05/54] WIP: untested replace of all encodes --- src/Wippersnapper.cpp | 26 +- .../analogIO/Wippersnapper_AnalogIO.cpp | 790 +++++++-------- src/components/ds18x20/ws_ds18x20.cpp | 620 ++++++------ src/components/pixels/ws_pixels.cpp | 904 +++++++++--------- .../register/Wippersnapper_Register.cpp | 4 +- .../uart/drivers/ws_uart_drv_pm25aqi.h | 428 ++++----- 6 files changed, 1386 insertions(+), 1386 deletions(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 948214139..88c2f6170 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -563,8 +563,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; } @@ -972,8 +972,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; } @@ -1137,8 +1137,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; } @@ -1544,8 +1544,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; } @@ -1635,8 +1635,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; } @@ -2803,9 +2803,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 diff --git a/src/components/analogIO/Wippersnapper_AnalogIO.cpp b/src/components/analogIO/Wippersnapper_AnalogIO.cpp index 86ceebced..4fb700bb4 100644 --- a/src/components/analogIO/Wippersnapper_AnalogIO.cpp +++ b/src/components/analogIO/Wippersnapper_AnalogIO.cpp @@ -1,396 +1,396 @@ -/*! - * @file Wippersnapper_AnalogIO.cpp - * - * This file provides an API for interacting with - * a board's analog IO pins. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2020-2023 for Adafruit Industries. - * - * BSD license, all text here must be included in any redistribution. - * - */ - -#include "Wippersnapper_AnalogIO.h" - -/***********************************************************************************/ -/*! - @brief Initializes Analog IO class. - @param totalAnalogInputPins - Total number of analog input pins to allocate. - @param aRef - ADC's voltage reference value, in volts. -*/ -/***********************************************************************************/ -Wippersnapper_AnalogIO::Wippersnapper_AnalogIO(int32_t totalAnalogInputPins, - float aRef) { - _totalAnalogInputPins = totalAnalogInputPins; - - // Set aref - setAref(aRef); - - // Set ADC resolution, default to 16-bit - setADCResolution(16); - - // allocate analog input pins - _analog_input_pins = new analogInputPin[_totalAnalogInputPins]; - - // TODO: Refactor this to use list-based initialization - for (int pin = 0; pin < _totalAnalogInputPins; pin++) { - // turn sampling off - _analog_input_pins[pin].enabled = false; - } -} - -/***********************************************************************************/ -/*! - @brief Destructor for Analog IO class. -*/ -/***********************************************************************************/ -Wippersnapper_AnalogIO::~Wippersnapper_AnalogIO() { - _aRef = 0.0; - _totalAnalogInputPins = 0; - delete _analog_input_pins; -} - -/***********************************************************************************/ -/*! - @brief Sets the device's reference voltage. - @param refVoltage - The voltage reference to use during conversions. -*/ -/***********************************************************************************/ -void Wippersnapper_AnalogIO::setAref(float refVoltage) { _aRef = refVoltage; } - -/***********************************************************************************/ -/*! - @brief Returns the device's reference voltage. - @returns Analog reference voltage, in volts. -*/ -/***********************************************************************************/ -float Wippersnapper_AnalogIO::getAref() { return _aRef; } - -/***********************************************************************************/ -/*! - @brief Sets the device's ADC resolution, either natively via calling - Arduino API's analogReadResolution() or via scaling. - @param resolution - The desired analog resolution, in bits. -*/ -/***********************************************************************************/ -void Wippersnapper_AnalogIO::setADCResolution(int resolution) { -// set the resolution natively in the BSP -#if defined(ARDUINO_ARCH_SAMD) - analogReadResolution(16); - _nativeResolution = 12; -#elif defined(ARDUINO_ARCH_ESP32) - scaleAnalogRead = true; - _nativeResolution = 13; -#endif - - _adcResolution = resolution; -} - -/***********************************************************************************/ -/*! - @brief Gets the scaled ADC resolution. - @returns resolution - The scaled analog resolution, in bits. -*/ -/***********************************************************************************/ -int Wippersnapper_AnalogIO::getADCresolution() { return _adcResolution; } - -/***********************************************************************************/ -/*! - @brief Gets the device's native ADC resolution. - @returns resolution - The native analog resolution, in bits. -*/ -/***********************************************************************************/ -int Wippersnapper_AnalogIO::getNativeResolution() { return _nativeResolution; } - -/***********************************************************************************/ -/*! - @brief Initializes an analog input pin - @param pin - The analog pin to read from. - @param period - Time between measurements, in seconds. - @param pullMode - The pin's pull value. - @param analogReadMode - Defines if pin will read and return an ADC value or a voltage value. -*/ -/***********************************************************************************/ -void Wippersnapper_AnalogIO::initAnalogInputPin( - int pin, float period, - wippersnapper_pin_v1_ConfigurePinRequest_Pull pullMode, - wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode analogReadMode) { - - // Set analog read pull mode - if (pullMode == wippersnapper_pin_v1_ConfigurePinRequest_Pull_PULL_UP) - pinMode(pin, INPUT_PULLUP); - else - pinMode(pin, INPUT); - - // Period is in seconds, cast it to long and convert it to milliseconds - long periodMs = (long)period * 1000; - - // TODO: Maybe pull this out into a func. or use map() lookup instead - // attempt to allocate pin within _analog_input_pins[] - for (int i = 0; i < _totalAnalogInputPins; i++) { - if (_analog_input_pins[i].enabled == false) { - _analog_input_pins[i].pinName = pin; - _analog_input_pins[i].period = periodMs; - _analog_input_pins[i].prvPeriod = 0L; - _analog_input_pins[i].readMode = analogReadMode; - _analog_input_pins[i].enabled = true; - break; - } - } - WS_DEBUG_PRINT("Configured Analog Input pin with polling time (ms):"); - WS_DEBUG_PRINTLN(periodMs); -} - -/***********************************************************************************/ -/*! - @brief Disables an analog input pin from sampling - @param pin - The analog input pin to disable. -*/ -/***********************************************************************************/ -void Wippersnapper_AnalogIO::disableAnalogInPin(int pin) { - for (int i = 0; i < _totalAnalogInputPins; i++) { - if (_analog_input_pins[i].pinName == pin) { - _analog_input_pins[i].enabled = false; - break; - } - } -} - -/***********************************************************************************/ -/*! - @brief Deinitializes an analog pin. - @param direction - The analog pin's direction. - @param pin - The analog pin to deinitialize. - -*/ -/***********************************************************************************/ -void Wippersnapper_AnalogIO::deinitAnalogPin( - wippersnapper_pin_v1_ConfigurePinRequest_Direction direction, int pin) { - WS_DEBUG_PRINT("Deinitializing analog pin A"); - WS_DEBUG_PRINTLN(pin); - if (direction == - wippersnapper_pin_v1_ConfigurePinRequest_Direction_DIRECTION_INPUT) { - WS_DEBUG_PRINTLN("Deinitialized analog input pin obj."); - disableAnalogInPin(pin); - } - pinMode(pin, INPUT); // hi-z -} - -/**********************************************************/ -/*! - @brief Reads the raw ADC value of an analog pin. - Value is always scaled to 16-bit. - @param pin - The pin to be read. - @returns The pin's ADC value. -*/ -/**********************************************************/ -uint16_t Wippersnapper_AnalogIO::getPinValue(int pin) { - // get pin value - uint16_t value = analogRead(pin); - - // scale by the ADC resolution manually if not implemented by BSP - if (scaleAnalogRead) { - if (getADCresolution() > getNativeResolution()) { - value = value << (getADCresolution() - getNativeResolution()); - } else { - value = value >> (getNativeResolution() - getADCresolution()); - } - } - return value; -} - -/**********************************************************/ -/*! - @brief Calculates analog pin's voltage provided - a 16-bit ADC value. - @param pin - The value from a previous ADC reading. - @returns The pin's voltage. -*/ -/**********************************************************/ -float Wippersnapper_AnalogIO::getPinValueVolts(int pin) { - uint16_t rawValue = getPinValue(pin); - return rawValue * getAref() / 65536; -} - -/******************************************************************/ -/*! - @brief Encodes an analog input pin event into a - signal message and publish it to IO. - @param pinName - Specifies the pin's name. - @param readMode - Read mode - raw ADC or voltage. - @param pinValRaw - Raw pin value, used if readmode is raw. - @param pinValVolts - Raw pin value expressed in Volts, used if readmode is - volts. - @returns True if successfully encoded a PinEvent signal - message, False otherwise. -*/ -/******************************************************************/ -bool Wippersnapper_AnalogIO::encodePinEvent( - uint8_t pinName, - wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode readMode, - uint16_t pinValRaw, float pinValVolts) { - // Create new signal message - wippersnapper_signal_v1_CreateSignalRequest outgoingSignalMsg = - wippersnapper_signal_v1_CreateSignalRequest_init_zero; - - // Fill payload - outgoingSignalMsg.which_payload = - wippersnapper_signal_v1_CreateSignalRequest_pin_event_tag; - sprintf(outgoingSignalMsg.payload.pin_event.pin_name, "A%d", pinName); - - // Fill pinValue based on the analog read mode - char buffer[100]; - if (readMode == - wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VALUE) { - sprintf(outgoingSignalMsg.payload.pin_event.pin_value, "%u", pinValRaw); - snprintf(buffer, 100, "[Pin] A%d read: %u\n", pinName, pinValRaw); - } else { - sprintf(outgoingSignalMsg.payload.pin_event.pin_value, "%0.3f", - pinValVolts); - snprintf(buffer, 100, "[Pin] A%d read: %0.2f\n", pinName, pinValVolts); - } -// display analog pin read on terminal -#ifdef USE_DISPLAY - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - // 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)) { - WS_DEBUG_PRINTLN("ERROR: Unable to encode signal message"); - return false; - } - - // Publish out to IO - size_t msgSz; - pb_get_encoded_size(&msgSz, - wippersnapper_signal_v1_CreateSignalRequest_fields, - &outgoingSignalMsg); - WS_DEBUG_PRINT("Publishing pinEvent..."); - WS.publish(WS._topic_signal_device, WS._buffer_outgoing, msgSz, 1); - WS_DEBUG_PRINTLN("Published!"); - - return true; -} - -/**********************************************************/ -/*! - @brief Checks if pin's period is expired. - @param currentTime - The current software timer value. - @param pin - The desired analog pin to check - @returns True if pin's period expired, False otherwise. -*/ -/**********************************************************/ -bool Wippersnapper_AnalogIO::timerExpired(long currentTime, - analogInputPin pin) { - if (currentTime - pin.prvPeriod > pin.period && pin.period != 0L) - return true; - return false; -} - -/**********************************************************/ -/*! - @brief Iterates thru analog inputs -*/ -/**********************************************************/ -void Wippersnapper_AnalogIO::update() { - // TODO: Globally scope these, dont have them here every time - float pinValVolts = 0.0; - uint16_t pinValRaw = 0; - // Process analog input pins - for (int i = 0; i < _totalAnalogInputPins; i++) { - // TODO: Can we collapse the conditionals below? - if (_analog_input_pins[i].enabled == true) { - - // Does the pin execute on-period? - if ((long)millis() - _analog_input_pins[i].prvPeriod > - _analog_input_pins[i].period && - _analog_input_pins[i].period != 0L) { - WS_DEBUG_PRINT("Executing periodic event on A"); - WS_DEBUG_PRINTLN(_analog_input_pins[i].pinName); - - // Read from analog pin - if (_analog_input_pins[i].readMode == - wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VOLTAGE) { - pinValVolts = getPinValueVolts(_analog_input_pins[i].pinName); - } else if ( - _analog_input_pins[i].readMode == - wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VALUE) { - pinValRaw = getPinValue(_analog_input_pins[i].pinName); - } else { - WS_DEBUG_PRINTLN("ERROR: Unable to read pin value, cannot determine " - "analog read mode!"); - pinValRaw = 0.0; - } - - // Publish a new pin event - encodePinEvent(_analog_input_pins[i].pinName, - _analog_input_pins[i].readMode, pinValRaw, pinValVolts); - - // IMPT - reset the digital pin - _analog_input_pins[i].prvPeriod = millis(); - } - // Does the pin execute on_change? - else if (_analog_input_pins[i].period == 0L) { - - // note: on-change requires ADC DEFAULT_HYSTERISIS to check against prv - // pin value - uint16_t pinValRaw = getPinValue(_analog_input_pins[i].pinName); - - uint16_t _pinValThreshHi = - _analog_input_pins[i].prvPinVal + - (_analog_input_pins[i].prvPinVal * DEFAULT_HYSTERISIS); - uint16_t _pinValThreshLow = - _analog_input_pins[i].prvPinVal - - (_analog_input_pins[i].prvPinVal * DEFAULT_HYSTERISIS); - - if (pinValRaw > _pinValThreshHi || pinValRaw < _pinValThreshLow) { - // Perform voltage conversion if we need to - if (_analog_input_pins[i].readMode == - wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VOLTAGE) { - pinValVolts = pinValRaw * getAref() / 65536; - } - - // Publish pin event to IO - encodePinEvent(_analog_input_pins[i].pinName, - _analog_input_pins[i].readMode, pinValRaw, - pinValVolts); - - } else { - // WS_DEBUG_PRINTLN("ADC has not changed enough, continue..."); - continue; - } - // set the pin value in the digital pin object for comparison on next - // run - _analog_input_pins[i].prvPinVal = pinValRaw; - } - } - } +/*! + * @file Wippersnapper_AnalogIO.cpp + * + * This file provides an API for interacting with + * a board's analog IO pins. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2020-2023 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#include "Wippersnapper_AnalogIO.h" + +/***********************************************************************************/ +/*! + @brief Initializes Analog IO class. + @param totalAnalogInputPins + Total number of analog input pins to allocate. + @param aRef + ADC's voltage reference value, in volts. +*/ +/***********************************************************************************/ +Wippersnapper_AnalogIO::Wippersnapper_AnalogIO(int32_t totalAnalogInputPins, + float aRef) { + _totalAnalogInputPins = totalAnalogInputPins; + + // Set aref + setAref(aRef); + + // Set ADC resolution, default to 16-bit + setADCResolution(16); + + // allocate analog input pins + _analog_input_pins = new analogInputPin[_totalAnalogInputPins]; + + // TODO: Refactor this to use list-based initialization + for (int pin = 0; pin < _totalAnalogInputPins; pin++) { + // turn sampling off + _analog_input_pins[pin].enabled = false; + } +} + +/***********************************************************************************/ +/*! + @brief Destructor for Analog IO class. +*/ +/***********************************************************************************/ +Wippersnapper_AnalogIO::~Wippersnapper_AnalogIO() { + _aRef = 0.0; + _totalAnalogInputPins = 0; + delete _analog_input_pins; +} + +/***********************************************************************************/ +/*! + @brief Sets the device's reference voltage. + @param refVoltage + The voltage reference to use during conversions. +*/ +/***********************************************************************************/ +void Wippersnapper_AnalogIO::setAref(float refVoltage) { _aRef = refVoltage; } + +/***********************************************************************************/ +/*! + @brief Returns the device's reference voltage. + @returns Analog reference voltage, in volts. +*/ +/***********************************************************************************/ +float Wippersnapper_AnalogIO::getAref() { return _aRef; } + +/***********************************************************************************/ +/*! + @brief Sets the device's ADC resolution, either natively via calling + Arduino API's analogReadResolution() or via scaling. + @param resolution + The desired analog resolution, in bits. +*/ +/***********************************************************************************/ +void Wippersnapper_AnalogIO::setADCResolution(int resolution) { +// set the resolution natively in the BSP +#if defined(ARDUINO_ARCH_SAMD) + analogReadResolution(16); + _nativeResolution = 12; +#elif defined(ARDUINO_ARCH_ESP32) + scaleAnalogRead = true; + _nativeResolution = 13; +#endif + + _adcResolution = resolution; +} + +/***********************************************************************************/ +/*! + @brief Gets the scaled ADC resolution. + @returns resolution + The scaled analog resolution, in bits. +*/ +/***********************************************************************************/ +int Wippersnapper_AnalogIO::getADCresolution() { return _adcResolution; } + +/***********************************************************************************/ +/*! + @brief Gets the device's native ADC resolution. + @returns resolution + The native analog resolution, in bits. +*/ +/***********************************************************************************/ +int Wippersnapper_AnalogIO::getNativeResolution() { return _nativeResolution; } + +/***********************************************************************************/ +/*! + @brief Initializes an analog input pin + @param pin + The analog pin to read from. + @param period + Time between measurements, in seconds. + @param pullMode + The pin's pull value. + @param analogReadMode + Defines if pin will read and return an ADC value or a voltage value. +*/ +/***********************************************************************************/ +void Wippersnapper_AnalogIO::initAnalogInputPin( + int pin, float period, + wippersnapper_pin_v1_ConfigurePinRequest_Pull pullMode, + wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode analogReadMode) { + + // Set analog read pull mode + if (pullMode == wippersnapper_pin_v1_ConfigurePinRequest_Pull_PULL_UP) + pinMode(pin, INPUT_PULLUP); + else + pinMode(pin, INPUT); + + // Period is in seconds, cast it to long and convert it to milliseconds + long periodMs = (long)period * 1000; + + // TODO: Maybe pull this out into a func. or use map() lookup instead + // attempt to allocate pin within _analog_input_pins[] + for (int i = 0; i < _totalAnalogInputPins; i++) { + if (_analog_input_pins[i].enabled == false) { + _analog_input_pins[i].pinName = pin; + _analog_input_pins[i].period = periodMs; + _analog_input_pins[i].prvPeriod = 0L; + _analog_input_pins[i].readMode = analogReadMode; + _analog_input_pins[i].enabled = true; + break; + } + } + WS_DEBUG_PRINT("Configured Analog Input pin with polling time (ms):"); + WS_DEBUG_PRINTLN(periodMs); +} + +/***********************************************************************************/ +/*! + @brief Disables an analog input pin from sampling + @param pin + The analog input pin to disable. +*/ +/***********************************************************************************/ +void Wippersnapper_AnalogIO::disableAnalogInPin(int pin) { + for (int i = 0; i < _totalAnalogInputPins; i++) { + if (_analog_input_pins[i].pinName == pin) { + _analog_input_pins[i].enabled = false; + break; + } + } +} + +/***********************************************************************************/ +/*! + @brief Deinitializes an analog pin. + @param direction + The analog pin's direction. + @param pin + The analog pin to deinitialize. + +*/ +/***********************************************************************************/ +void Wippersnapper_AnalogIO::deinitAnalogPin( + wippersnapper_pin_v1_ConfigurePinRequest_Direction direction, int pin) { + WS_DEBUG_PRINT("Deinitializing analog pin A"); + WS_DEBUG_PRINTLN(pin); + if (direction == + wippersnapper_pin_v1_ConfigurePinRequest_Direction_DIRECTION_INPUT) { + WS_DEBUG_PRINTLN("Deinitialized analog input pin obj."); + disableAnalogInPin(pin); + } + pinMode(pin, INPUT); // hi-z +} + +/**********************************************************/ +/*! + @brief Reads the raw ADC value of an analog pin. + Value is always scaled to 16-bit. + @param pin + The pin to be read. + @returns The pin's ADC value. +*/ +/**********************************************************/ +uint16_t Wippersnapper_AnalogIO::getPinValue(int pin) { + // get pin value + uint16_t value = analogRead(pin); + + // scale by the ADC resolution manually if not implemented by BSP + if (scaleAnalogRead) { + if (getADCresolution() > getNativeResolution()) { + value = value << (getADCresolution() - getNativeResolution()); + } else { + value = value >> (getNativeResolution() - getADCresolution()); + } + } + return value; +} + +/**********************************************************/ +/*! + @brief Calculates analog pin's voltage provided + a 16-bit ADC value. + @param pin + The value from a previous ADC reading. + @returns The pin's voltage. +*/ +/**********************************************************/ +float Wippersnapper_AnalogIO::getPinValueVolts(int pin) { + uint16_t rawValue = getPinValue(pin); + return rawValue * getAref() / 65536; +} + +/******************************************************************/ +/*! + @brief Encodes an analog input pin event into a + signal message and publish it to IO. + @param pinName + Specifies the pin's name. + @param readMode + Read mode - raw ADC or voltage. + @param pinValRaw + Raw pin value, used if readmode is raw. + @param pinValVolts + Raw pin value expressed in Volts, used if readmode is + volts. + @returns True if successfully encoded a PinEvent signal + message, False otherwise. +*/ +/******************************************************************/ +bool Wippersnapper_AnalogIO::encodePinEvent( + uint8_t pinName, + wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode readMode, + uint16_t pinValRaw, float pinValVolts) { + // Create new signal message + wippersnapper_signal_v1_CreateSignalRequest outgoingSignalMsg = + wippersnapper_signal_v1_CreateSignalRequest_init_zero; + + // Fill payload + outgoingSignalMsg.which_payload = + wippersnapper_signal_v1_CreateSignalRequest_pin_event_tag; + sprintf(outgoingSignalMsg.payload.pin_event.pin_name, "A%d", pinName); + + // Fill pinValue based on the analog read mode + char buffer[100]; + if (readMode == + wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VALUE) { + sprintf(outgoingSignalMsg.payload.pin_event.pin_value, "%u", pinValRaw); + snprintf(buffer, 100, "[Pin] A%d read: %u\n", pinName, pinValRaw); + } else { + sprintf(outgoingSignalMsg.payload.pin_event.pin_value, "%0.3f", + pinValVolts); + snprintf(buffer, 100, "[Pin] A%d read: %0.2f\n", pinName, pinValVolts); + } +// display analog pin read on terminal +#ifdef USE_DISPLAY + WS._ui_helper->add_text_to_terminal(buffer); +#endif + + // Encode signal message + pb_ostream_t stream = + pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); + if (!ws_pb_encode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, + &outgoingSignalMsg)) { + WS_DEBUG_PRINTLN("ERROR: Unable to encode signal message"); + return false; + } + + // Publish out to IO + size_t msgSz; + pb_get_encoded_size(&msgSz, + wippersnapper_signal_v1_CreateSignalRequest_fields, + &outgoingSignalMsg); + WS_DEBUG_PRINT("Publishing pinEvent..."); + WS.publish(WS._topic_signal_device, WS._buffer_outgoing, msgSz, 1); + WS_DEBUG_PRINTLN("Published!"); + + return true; +} + +/**********************************************************/ +/*! + @brief Checks if pin's period is expired. + @param currentTime + The current software timer value. + @param pin + The desired analog pin to check + @returns True if pin's period expired, False otherwise. +*/ +/**********************************************************/ +bool Wippersnapper_AnalogIO::timerExpired(long currentTime, + analogInputPin pin) { + if (currentTime - pin.prvPeriod > pin.period && pin.period != 0L) + return true; + return false; +} + +/**********************************************************/ +/*! + @brief Iterates thru analog inputs +*/ +/**********************************************************/ +void Wippersnapper_AnalogIO::update() { + // TODO: Globally scope these, dont have them here every time + float pinValVolts = 0.0; + uint16_t pinValRaw = 0; + // Process analog input pins + for (int i = 0; i < _totalAnalogInputPins; i++) { + // TODO: Can we collapse the conditionals below? + if (_analog_input_pins[i].enabled == true) { + + // Does the pin execute on-period? + if ((long)millis() - _analog_input_pins[i].prvPeriod > + _analog_input_pins[i].period && + _analog_input_pins[i].period != 0L) { + WS_DEBUG_PRINT("Executing periodic event on A"); + WS_DEBUG_PRINTLN(_analog_input_pins[i].pinName); + + // Read from analog pin + if (_analog_input_pins[i].readMode == + wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VOLTAGE) { + pinValVolts = getPinValueVolts(_analog_input_pins[i].pinName); + } else if ( + _analog_input_pins[i].readMode == + wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VALUE) { + pinValRaw = getPinValue(_analog_input_pins[i].pinName); + } else { + WS_DEBUG_PRINTLN("ERROR: Unable to read pin value, cannot determine " + "analog read mode!"); + pinValRaw = 0.0; + } + + // Publish a new pin event + encodePinEvent(_analog_input_pins[i].pinName, + _analog_input_pins[i].readMode, pinValRaw, pinValVolts); + + // IMPT - reset the digital pin + _analog_input_pins[i].prvPeriod = millis(); + } + // Does the pin execute on_change? + else if (_analog_input_pins[i].period == 0L) { + + // note: on-change requires ADC DEFAULT_HYSTERISIS to check against prv + // pin value + uint16_t pinValRaw = getPinValue(_analog_input_pins[i].pinName); + + uint16_t _pinValThreshHi = + _analog_input_pins[i].prvPinVal + + (_analog_input_pins[i].prvPinVal * DEFAULT_HYSTERISIS); + uint16_t _pinValThreshLow = + _analog_input_pins[i].prvPinVal - + (_analog_input_pins[i].prvPinVal * DEFAULT_HYSTERISIS); + + if (pinValRaw > _pinValThreshHi || pinValRaw < _pinValThreshLow) { + // Perform voltage conversion if we need to + if (_analog_input_pins[i].readMode == + wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VOLTAGE) { + pinValVolts = pinValRaw * getAref() / 65536; + } + + // Publish pin event to IO + encodePinEvent(_analog_input_pins[i].pinName, + _analog_input_pins[i].readMode, pinValRaw, + pinValVolts); + + } else { + // WS_DEBUG_PRINTLN("ADC has not changed enough, continue..."); + continue; + } + // set the pin value in the digital pin object for comparison on next + // run + _analog_input_pins[i].prvPinVal = pinValRaw; + } + } + } } \ No newline at end of file diff --git a/src/components/ds18x20/ws_ds18x20.cpp b/src/components/ds18x20/ws_ds18x20.cpp index 91360271d..7c9d4561d 100644 --- a/src/components/ds18x20/ws_ds18x20.cpp +++ b/src/components/ds18x20/ws_ds18x20.cpp @@ -1,311 +1,311 @@ -/*! - * @file ws_ds18x20.cpp - * - * This component implements 1-wire communication - * for the DS18X20-line of Maxim Temperature ICs. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2022-2023 for Adafruit Industries. - * - * BSD license, all text here must be included in any redistribution. - * - */ - -#include "ws_ds18x20.h" - -/*************************************************************/ -/*! - @brief Creates a new WipperSnapper Ds18x20 component. -*/ -/*************************************************************/ -ws_ds18x20::ws_ds18x20() {} - -/*************************************************************/ -/*! - @brief Destructor for a WipperSnapper DS18X20 component. -*/ -/*************************************************************/ -ws_ds18x20::~ws_ds18x20() { - // delete DallasTemp sensors and release onewire buses - for (size_t idx = 0; idx < _ds18xDrivers.size(); idx++) { - delete _ds18xDrivers[idx]->dallasTempObj; - delete _ds18xDrivers[idx]->oneWire; - } - // remove all elements - _ds18xDrivers.clear(); -} - -/********************************************************************/ -/*! - @brief Initializes a DS18x20 sensor using a - configuration sent by the broker and adds it to a - vector of ds18x20 sensor drivers. - @param msgDs18x20InitReq - Message containing configuration data for a - ds18x20 sensor. - @returns True if initialized successfully, False otherwise. -*/ -/********************************************************************/ -bool ws_ds18x20::addDS18x20( - wippersnapper_ds18x20_v1_Ds18x20InitRequest *msgDs18x20InitReq) { - bool is_success = false; - - // init. new ds18x20 object - ds18x20Obj *newObj = new ds18x20Obj(); - char *oneWirePin = msgDs18x20InitReq->onewire_pin + 1; - newObj->oneWire = new OneWire(atoi(oneWirePin)); - newObj->dallasTempObj = new DallasTemperature(newObj->oneWire); - newObj->dallasTempObj->begin(); - // attempt to obtain sensor address - if (newObj->dallasTempObj->getAddress(newObj->dallasTempAddr, 0)) { - // attempt to set sensor resolution - newObj->dallasTempObj->setResolution(msgDs18x20InitReq->sensor_resolution); - // copy the device's sensor properties - newObj->sensorPropertiesCount = - msgDs18x20InitReq->i2c_device_properties_count; - // TODO: Make sure this works, it's a new idea and untested :) - for (int i = 0; i < newObj->sensorPropertiesCount; i++) { - newObj->sensorProperties[i].sensor_type = - msgDs18x20InitReq->i2c_device_properties[i].sensor_type; - newObj->sensorProperties[i].sensor_period = - (long)msgDs18x20InitReq->i2c_device_properties[i].sensor_period * - 1000; - } - // set pin - strcpy(newObj->onewire_pin, msgDs18x20InitReq->onewire_pin); - // add the new ds18x20 driver to vec. - _ds18xDrivers.push_back(newObj); - is_success = true; - } else { - WS_DEBUG_PRINTLN("Failed to find DSx sensor on specified pin."); - } - - // fill and publish the initialization response back to the broker - size_t msgSz; // message's encoded size - wippersnapper_signal_v1_Ds18x20Response msgInitResp = - wippersnapper_signal_v1_Ds18x20Response_init_zero; - msgInitResp.which_payload = - wippersnapper_signal_v1_Ds18x20Response_resp_ds18x20_init_tag; - msgInitResp.payload.resp_ds18x20_init.is_initialized = is_success; - strcpy(msgInitResp.payload.resp_ds18x20_init.onewire_pin, - msgDs18x20InitReq->onewire_pin); - - WS_DEBUG_PRINT("Created OneWireBus on GPIO "); - WS_DEBUG_PRINT(msgDs18x20InitReq->onewire_pin); - WS_DEBUG_PRINTLN(" with DS18x20 attached!"); - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[DS18x] Attached DS18x20 sensor to pin %s\n", - msgDs18x20InitReq->onewire_pin); - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - // Encode and publish response back to broker - 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)) { - WS_DEBUG_PRINTLN("ERROR: Unable to encode msg_init response message!"); - return false; - } - pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_Ds18x20Response_fields, - &msgInitResp); - WS_DEBUG_PRINT("-> DS18x Init Response..."); - WS._mqtt->publish(WS._topic_signal_ds18_device, WS._buffer_outgoing, msgSz, - 1); - WS_DEBUG_PRINTLN("Published!"); - - return is_success; -} - -/********************************************************************/ -/*! - @brief De-initializes a DS18x20 sensor and releases its - pin and resources. - @param msgDS18x20DeinitReq - Message containing configuration data for a - ds18x20 sensor. -*/ -/********************************************************************/ -void ws_ds18x20::deleteDS18x20( - wippersnapper_ds18x20_v1_Ds18x20DeInitRequest *msgDS18x20DeinitReq) { - // Loop thru vector of drivers to find the unique address - for (size_t idx = 0; idx < _ds18xDrivers.size(); idx++) { - if (strcmp(_ds18xDrivers[idx]->onewire_pin, - msgDS18x20DeinitReq->onewire_pin) == 0) { - WS_DEBUG_PRINT("Deleting OneWire instance on pin "); - WS_DEBUG_PRINTLN(msgDS18x20DeinitReq->onewire_pin); - delete _ds18xDrivers[idx] - ->dallasTempObj; // delete dallas temp instance on pin - delete _ds18xDrivers[idx] - ->oneWire; // delete OneWire instance on pin and release pin for reuse - _ds18xDrivers.erase(_ds18xDrivers.begin() + - idx); // erase vector and re-allocate - } - } - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[DS18x] Deleted DS18x20 sensor on pin %s\n", - msgDS18x20DeinitReq->onewire_pin); - WS._ui_helper->add_text_to_terminal(buffer); -#endif -} - -/*************************************************************/ -/*! - @brief Iterates through each ds18x20 sensor and - reports data (if period expired) to Adafruit IO. -*/ -/*************************************************************/ -void ws_ds18x20::update() { - // return immediately if no drivers have been initialized - if (_ds18xDrivers.size() == 0) - return; - - long curTime; // used for holding the millis() value - std::vector::iterator iter, end; - for (iter = _ds18xDrivers.begin(), end = _ds18xDrivers.end(); iter != end; - ++iter) { - - // Create an empty DS18x20 event signal message and configure - wippersnapper_signal_v1_Ds18x20Response msgDS18x20Response = - wippersnapper_signal_v1_Ds18x20Response_init_zero; - msgDS18x20Response.which_payload = - wippersnapper_signal_v1_Ds18x20Response_resp_ds18x20_event_tag; - - // take the current time for the driver (*iter) - curTime = millis(); - // Poll each sensor type, if period has elapsed - for (int i = 0; i < (*iter)->sensorPropertiesCount; i++) { - // has sensor_period elapsed? - if (curTime - (*iter)->sensorPeriodPrv > - (long)(*iter)->sensorProperties[i].sensor_period) { - // issue global temperature request to all DS sensors - WS_DEBUG_PRINTLN("Requesting temperature.."); - (*iter)->dallasTempObj->requestTemperatures(); - // poll the DS sensor driver - float tempC = (*iter)->dallasTempObj->getTempC((*iter)->dallasTempAddr); - if (tempC == DEVICE_DISCONNECTED_C) { - WS_DEBUG_PRINTLN("ERROR: Could not read temperature data, is the " - "sensor disconnected?"); -#ifdef USE_DISPLAY - WS._ui_helper->add_text_to_terminal( - "[DS18x ERROR] Unable to read temperature, is the sensor " - "disconnected?\n"); -#endif - break; - } - - // check and pack based on sensorType - char buffer[100]; - if ((*iter)->sensorProperties[i].sensor_type == - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE) { - - WS_DEBUG_PRINT("(OneWireBus GPIO: "); - WS_DEBUG_PRINT((*iter)->onewire_pin); - WS_DEBUG_PRINT(") DS18x20 Value: "); - WS_DEBUG_PRINT(tempC); - WS_DEBUG_PRINTLN("*C") - snprintf(buffer, 100, "[DS18x] Read %0.2f*C on GPIO %s\n", tempC, - (*iter)->onewire_pin); - - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].type = - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE; - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].value = - tempC; - - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count++; - } - - if ((*iter)->sensorProperties[i].sensor_type == - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT) { - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].type = - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT; - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].value = - (*iter)->dallasTempObj->toFahrenheit(tempC); - WS_DEBUG_PRINT("(OneWireBus GPIO: "); - WS_DEBUG_PRINT((*iter)->onewire_pin); - WS_DEBUG_PRINT(") DS18x20 Value: "); - WS_DEBUG_PRINT( - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] - .value); - WS_DEBUG_PRINTLN("*F") - snprintf(buffer, 100, "[DS18x] Read %0.2f*F on GPIO %s\n", - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] - .value, - (*iter)->onewire_pin); - - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count++; - } - - // did we obtain the expected amount of sensor events for the - // `resp_ds18x20_event` message? - if (msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count == - (*iter)->sensorPropertiesCount) { - - // prep sensor event data for sending to IO - // use onewire_pin as the "address" - strcpy(msgDS18x20Response.payload.resp_ds18x20_event.onewire_pin, - (*iter)->onewire_pin); - // prep and encode buffer - 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)) { - WS_DEBUG_PRINTLN( - "ERROR: Unable to encode DS18x20 event responsemessage!"); - snprintf(buffer, 100, - "[DS18x ERROR] Unable to encode event message!"); - return; - } - - WS_DEBUG_PRINTLN( - "DEBUG: msgDS18x20Response sensor_event message contents:"); - for (int i = 0; - i < - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count; - i++) { - WS_DEBUG_PRINT("sensor_event[#]: "); - WS_DEBUG_PRINTLN(i); - WS_DEBUG_PRINT("\tOneWire Bus: "); - WS_DEBUG_PRINTLN( - msgDS18x20Response.payload.resp_ds18x20_event.onewire_pin); - WS_DEBUG_PRINT("\tsensor_event type: "); - WS_DEBUG_PRINTLN( - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] - .type); - WS_DEBUG_PRINT("\tsensor_event value: "); - WS_DEBUG_PRINTLN( - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] - .value); - } - - // Publish I2CResponse msg - size_t msgSz; - pb_get_encoded_size(&msgSz, - wippersnapper_signal_v1_Ds18x20Response_fields, - &msgDS18x20Response); - WS_DEBUG_PRINT("PUBLISHING -> msgDS18x20Response Event Message..."); - if (!WS._mqtt->publish(WS._topic_signal_ds18_device, - WS._buffer_outgoing, msgSz, 1)) { - return; - }; - WS_DEBUG_PRINTLN("PUBLISHED!"); -#ifdef USE_DISPLAY - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - (*iter)->sensorPeriodPrv = curTime; // set prv period - } - } - } - } +/*! + * @file ws_ds18x20.cpp + * + * This component implements 1-wire communication + * for the DS18X20-line of Maxim Temperature ICs. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2022-2023 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#include "ws_ds18x20.h" + +/*************************************************************/ +/*! + @brief Creates a new WipperSnapper Ds18x20 component. +*/ +/*************************************************************/ +ws_ds18x20::ws_ds18x20() {} + +/*************************************************************/ +/*! + @brief Destructor for a WipperSnapper DS18X20 component. +*/ +/*************************************************************/ +ws_ds18x20::~ws_ds18x20() { + // delete DallasTemp sensors and release onewire buses + for (size_t idx = 0; idx < _ds18xDrivers.size(); idx++) { + delete _ds18xDrivers[idx]->dallasTempObj; + delete _ds18xDrivers[idx]->oneWire; + } + // remove all elements + _ds18xDrivers.clear(); +} + +/********************************************************************/ +/*! + @brief Initializes a DS18x20 sensor using a + configuration sent by the broker and adds it to a + vector of ds18x20 sensor drivers. + @param msgDs18x20InitReq + Message containing configuration data for a + ds18x20 sensor. + @returns True if initialized successfully, False otherwise. +*/ +/********************************************************************/ +bool ws_ds18x20::addDS18x20( + wippersnapper_ds18x20_v1_Ds18x20InitRequest *msgDs18x20InitReq) { + bool is_success = false; + + // init. new ds18x20 object + ds18x20Obj *newObj = new ds18x20Obj(); + char *oneWirePin = msgDs18x20InitReq->onewire_pin + 1; + newObj->oneWire = new OneWire(atoi(oneWirePin)); + newObj->dallasTempObj = new DallasTemperature(newObj->oneWire); + newObj->dallasTempObj->begin(); + // attempt to obtain sensor address + if (newObj->dallasTempObj->getAddress(newObj->dallasTempAddr, 0)) { + // attempt to set sensor resolution + newObj->dallasTempObj->setResolution(msgDs18x20InitReq->sensor_resolution); + // copy the device's sensor properties + newObj->sensorPropertiesCount = + msgDs18x20InitReq->i2c_device_properties_count; + // TODO: Make sure this works, it's a new idea and untested :) + for (int i = 0; i < newObj->sensorPropertiesCount; i++) { + newObj->sensorProperties[i].sensor_type = + msgDs18x20InitReq->i2c_device_properties[i].sensor_type; + newObj->sensorProperties[i].sensor_period = + (long)msgDs18x20InitReq->i2c_device_properties[i].sensor_period * + 1000; + } + // set pin + strcpy(newObj->onewire_pin, msgDs18x20InitReq->onewire_pin); + // add the new ds18x20 driver to vec. + _ds18xDrivers.push_back(newObj); + is_success = true; + } else { + WS_DEBUG_PRINTLN("Failed to find DSx sensor on specified pin."); + } + + // fill and publish the initialization response back to the broker + size_t msgSz; // message's encoded size + wippersnapper_signal_v1_Ds18x20Response msgInitResp = + wippersnapper_signal_v1_Ds18x20Response_init_zero; + msgInitResp.which_payload = + wippersnapper_signal_v1_Ds18x20Response_resp_ds18x20_init_tag; + msgInitResp.payload.resp_ds18x20_init.is_initialized = is_success; + strcpy(msgInitResp.payload.resp_ds18x20_init.onewire_pin, + msgDs18x20InitReq->onewire_pin); + + WS_DEBUG_PRINT("Created OneWireBus on GPIO "); + WS_DEBUG_PRINT(msgDs18x20InitReq->onewire_pin); + WS_DEBUG_PRINTLN(" with DS18x20 attached!"); + +#ifdef USE_DISPLAY + char buffer[100]; + snprintf(buffer, 100, "[DS18x] Attached DS18x20 sensor to pin %s\n", + msgDs18x20InitReq->onewire_pin); + WS._ui_helper->add_text_to_terminal(buffer); +#endif + + // Encode and publish response back to broker + 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 (!ws_pb_encode(&ostream, wippersnapper_signal_v1_Ds18x20Response_fields, + &msgInitResp)) { + WS_DEBUG_PRINTLN("ERROR: Unable to encode msg_init response message!"); + return false; + } + pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_Ds18x20Response_fields, + &msgInitResp); + WS_DEBUG_PRINT("-> DS18x Init Response..."); + WS._mqtt->publish(WS._topic_signal_ds18_device, WS._buffer_outgoing, msgSz, + 1); + WS_DEBUG_PRINTLN("Published!"); + + return is_success; +} + +/********************************************************************/ +/*! + @brief De-initializes a DS18x20 sensor and releases its + pin and resources. + @param msgDS18x20DeinitReq + Message containing configuration data for a + ds18x20 sensor. +*/ +/********************************************************************/ +void ws_ds18x20::deleteDS18x20( + wippersnapper_ds18x20_v1_Ds18x20DeInitRequest *msgDS18x20DeinitReq) { + // Loop thru vector of drivers to find the unique address + for (size_t idx = 0; idx < _ds18xDrivers.size(); idx++) { + if (strcmp(_ds18xDrivers[idx]->onewire_pin, + msgDS18x20DeinitReq->onewire_pin) == 0) { + WS_DEBUG_PRINT("Deleting OneWire instance on pin "); + WS_DEBUG_PRINTLN(msgDS18x20DeinitReq->onewire_pin); + delete _ds18xDrivers[idx] + ->dallasTempObj; // delete dallas temp instance on pin + delete _ds18xDrivers[idx] + ->oneWire; // delete OneWire instance on pin and release pin for reuse + _ds18xDrivers.erase(_ds18xDrivers.begin() + + idx); // erase vector and re-allocate + } + } + +#ifdef USE_DISPLAY + char buffer[100]; + snprintf(buffer, 100, "[DS18x] Deleted DS18x20 sensor on pin %s\n", + msgDS18x20DeinitReq->onewire_pin); + WS._ui_helper->add_text_to_terminal(buffer); +#endif +} + +/*************************************************************/ +/*! + @brief Iterates through each ds18x20 sensor and + reports data (if period expired) to Adafruit IO. +*/ +/*************************************************************/ +void ws_ds18x20::update() { + // return immediately if no drivers have been initialized + if (_ds18xDrivers.size() == 0) + return; + + long curTime; // used for holding the millis() value + std::vector::iterator iter, end; + for (iter = _ds18xDrivers.begin(), end = _ds18xDrivers.end(); iter != end; + ++iter) { + + // Create an empty DS18x20 event signal message and configure + wippersnapper_signal_v1_Ds18x20Response msgDS18x20Response = + wippersnapper_signal_v1_Ds18x20Response_init_zero; + msgDS18x20Response.which_payload = + wippersnapper_signal_v1_Ds18x20Response_resp_ds18x20_event_tag; + + // take the current time for the driver (*iter) + curTime = millis(); + // Poll each sensor type, if period has elapsed + for (int i = 0; i < (*iter)->sensorPropertiesCount; i++) { + // has sensor_period elapsed? + if (curTime - (*iter)->sensorPeriodPrv > + (long)(*iter)->sensorProperties[i].sensor_period) { + // issue global temperature request to all DS sensors + WS_DEBUG_PRINTLN("Requesting temperature.."); + (*iter)->dallasTempObj->requestTemperatures(); + // poll the DS sensor driver + float tempC = (*iter)->dallasTempObj->getTempC((*iter)->dallasTempAddr); + if (tempC == DEVICE_DISCONNECTED_C) { + WS_DEBUG_PRINTLN("ERROR: Could not read temperature data, is the " + "sensor disconnected?"); +#ifdef USE_DISPLAY + WS._ui_helper->add_text_to_terminal( + "[DS18x ERROR] Unable to read temperature, is the sensor " + "disconnected?\n"); +#endif + break; + } + + // check and pack based on sensorType + char buffer[100]; + if ((*iter)->sensorProperties[i].sensor_type == + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE) { + + WS_DEBUG_PRINT("(OneWireBus GPIO: "); + WS_DEBUG_PRINT((*iter)->onewire_pin); + WS_DEBUG_PRINT(") DS18x20 Value: "); + WS_DEBUG_PRINT(tempC); + WS_DEBUG_PRINTLN("*C") + snprintf(buffer, 100, "[DS18x] Read %0.2f*C on GPIO %s\n", tempC, + (*iter)->onewire_pin); + + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].type = + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE; + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].value = + tempC; + + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count++; + } + + if ((*iter)->sensorProperties[i].sensor_type == + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT) { + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].type = + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT; + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].value = + (*iter)->dallasTempObj->toFahrenheit(tempC); + WS_DEBUG_PRINT("(OneWireBus GPIO: "); + WS_DEBUG_PRINT((*iter)->onewire_pin); + WS_DEBUG_PRINT(") DS18x20 Value: "); + WS_DEBUG_PRINT( + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] + .value); + WS_DEBUG_PRINTLN("*F") + snprintf(buffer, 100, "[DS18x] Read %0.2f*F on GPIO %s\n", + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] + .value, + (*iter)->onewire_pin); + + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count++; + } + + // did we obtain the expected amount of sensor events for the + // `resp_ds18x20_event` message? + if (msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count == + (*iter)->sensorPropertiesCount) { + + // prep sensor event data for sending to IO + // use onewire_pin as the "address" + strcpy(msgDS18x20Response.payload.resp_ds18x20_event.onewire_pin, + (*iter)->onewire_pin); + // prep and encode buffer + 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 (!ws_pb_encode(&ostream, + wippersnapper_signal_v1_Ds18x20Response_fields, + &msgDS18x20Response)) { + WS_DEBUG_PRINTLN( + "ERROR: Unable to encode DS18x20 event responsemessage!"); + snprintf(buffer, 100, + "[DS18x ERROR] Unable to encode event message!"); + return; + } + + WS_DEBUG_PRINTLN( + "DEBUG: msgDS18x20Response sensor_event message contents:"); + for (int i = 0; + i < + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count; + i++) { + WS_DEBUG_PRINT("sensor_event[#]: "); + WS_DEBUG_PRINTLN(i); + WS_DEBUG_PRINT("\tOneWire Bus: "); + WS_DEBUG_PRINTLN( + msgDS18x20Response.payload.resp_ds18x20_event.onewire_pin); + WS_DEBUG_PRINT("\tsensor_event type: "); + WS_DEBUG_PRINTLN( + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] + .type); + WS_DEBUG_PRINT("\tsensor_event value: "); + WS_DEBUG_PRINTLN( + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] + .value); + } + + // Publish I2CResponse msg + size_t msgSz; + pb_get_encoded_size(&msgSz, + wippersnapper_signal_v1_Ds18x20Response_fields, + &msgDS18x20Response); + WS_DEBUG_PRINT("PUBLISHING -> msgDS18x20Response Event Message..."); + if (!WS._mqtt->publish(WS._topic_signal_ds18_device, + WS._buffer_outgoing, msgSz, 1)) { + return; + }; + WS_DEBUG_PRINTLN("PUBLISHED!"); +#ifdef USE_DISPLAY + WS._ui_helper->add_text_to_terminal(buffer); +#endif + + (*iter)->sensorPeriodPrv = curTime; // set prv period + } + } + } + } } \ No newline at end of file diff --git a/src/components/pixels/ws_pixels.cpp b/src/components/pixels/ws_pixels.cpp index 73db519af..c77149ba1 100644 --- a/src/components/pixels/ws_pixels.cpp +++ b/src/components/pixels/ws_pixels.cpp @@ -1,453 +1,453 @@ -/*! - * @file ws_pixels.cpp - * - * High-level interface for wippersnapper to manage addressable RGB pixel - * strands - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * - * Written by Brent Rubell for Adafruit Industries, 2022-2023 - * - * MIT license, all text here must be included in any redistribution. - * - */ -#include "ws_pixels.h" - -strand_s strands[MAX_PIXEL_STRANDS]{ - nullptr, - nullptr, - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_UNSPECIFIED, - 0, - 0, - wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_UNSPECIFIED, - -1, - -1, - -1}; ///< Contains all pixel strands used by WipperSnapper - -/**************************************************************************/ -/*! - @brief Destructor -*/ -/**************************************************************************/ -ws_pixels::~ws_pixels() { - // de-allocate all strands - for (size_t i = 0; i < sizeof(strands) / sizeof(strands[0]); i++) - deallocateStrand(i); -} - -/******************************************************************************/ -/*! - @brief Allocates an index of a free strand_t within the strand array. - @returns Index of a free strand_t, ERR_INVALID_STRAND if strand array is - full. -*/ -/******************************************************************************/ -int16_t ws_pixels::allocateStrand() { - for (size_t strandIdx = 0; strandIdx < sizeof(strands) / sizeof(strands[0]); - strandIdx++) { - if (strands[strandIdx].type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_UNSPECIFIED) { - return strandIdx; - } - } - return ERR_INVALID_STRAND; -} - -/**************************************************************************/ -/*! - @brief Deallocates a `strand_t` within `strands`, provided an index. - @param strandIdx - The desired index of a `strand_t` within `strands`. -*/ -/**************************************************************************/ -void ws_pixels::deallocateStrand(int16_t strandIdx) { - - // delete the pixel object - if (strands[strandIdx].neoPixelPtr != nullptr) - delete strands[strandIdx].neoPixelPtr; - if ((strands[strandIdx].dotStarPtr != nullptr)) - delete strands[strandIdx].dotStarPtr; - - // re-initialize status pixel (if pixel was prvsly used) - if (strands[strandIdx].pinNeoPixel == getStatusNeoPixelPin() || - strands[strandIdx].pinDotStarData == getStatusDotStarDataPin()) { - initStatusLED(); - } - - // reset the strand - strands[strandIdx] = { - nullptr, - nullptr, - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_UNSPECIFIED, - 0, - 0, - wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_UNSPECIFIED, - -1, - -1, - -1}; -} - -/**************************************************************************/ -/*! - @brief Returns the `neoPixelType` provided the strand's pixelOrder - @param pixelOrder - Desired pixel order, from init. message. - @returns Type of NeoPixel strand, usable by Adafruit_NeoPixel - constructor -*/ -/**************************************************************************/ -neoPixelType ws_pixels::getNeoPixelStrandOrder( - wippersnapper_pixels_v1_PixelsOrder pixelOrder) { - switch (pixelOrder) { - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GRB: - return NEO_GRB + NEO_KHZ800; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GRBW: - return NEO_GRBW + NEO_KHZ800; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RGB: - return NEO_RGB + NEO_KHZ800; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RGBW: - return NEO_RGBW + NEO_KHZ800; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_BRG: - return NEO_BRG + NEO_KHZ800; - default: - return NEO_GRB + NEO_KHZ800; - } -} - -/**************************************************************************/ -/*! - @brief Returns the color order of the DotStar strand. - @param pixelOrder - Desired pixel order, from init. message. - @returns Type of DotStar strand. -*/ -/**************************************************************************/ -uint8_t ws_pixels::getDotStarStrandOrder( - wippersnapper_pixels_v1_PixelsOrder pixelOrder) { - switch (pixelOrder) { - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GRB: - return DOTSTAR_GRB; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RGB: - return DOTSTAR_RGB; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_BRG: - return DOTSTAR_BRG; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RBG: - return DOTSTAR_RBG; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GBR: - return DOTSTAR_GBR; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_BGR: - return DOTSTAR_BGR; - default: - return DOTSTAR_BRG; - } -} - -/**************************************************************************/ -/*! - @brief Creates a PixelsResponse message and publishes it to IO. - @param is_success - True if `addStrand()` succeeded, False otherwise. - @param pixels_pin_data - The strand's data pin.. -*/ -/**************************************************************************/ -void ws_pixels::publishAddStrandResponse(bool is_success, - char *pixels_pin_data) { - // Create response message - wippersnapper_signal_v1_PixelsResponse msgInitResp = - wippersnapper_signal_v1_PixelsResponse_init_zero; - msgInitResp.which_payload = - wippersnapper_signal_v1_PixelsResponse_resp_pixels_create_tag; - // Fill response message - msgInitResp.payload.resp_pixels_create.is_success = is_success; - memcpy(msgInitResp.payload.resp_pixels_create.pixels_pin_data, - pixels_pin_data, sizeof(char) * 6); - - // Encode `wippersnapper_pixels_v1_PixelsCreateResponse` message - 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)) { - WS_DEBUG_PRINTLN("ERROR: Unable to encode " - "wippersnapper_signal_v1_PixelsResponse message!"); - return; - } - - // Publish message to broker - size_t msgSz; - pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_PixelsResponse_fields, - &msgInitResp); - WS_DEBUG_PRINT("-> wippersnapper_signal_v1_PixelsResponse..."); - WS._mqtt->publish(WS._topic_signal_pixels_device, WS._buffer_outgoing, msgSz, - 1); - WS_DEBUG_PRINTLN("Published!"); -} - -/**************************************************************************/ -/*! - @brief Initializes a strand of addressable RGB Pixels. - @param pixelsCreateReqMsg - Pointer to strand init. request message - @returns True if successfully initialized, False otherwise. -*/ -/**************************************************************************/ -bool ws_pixels::addStrand( - wippersnapper_pixels_v1_PixelsCreateRequest *pixelsCreateReqMsg) { - // attempt to allocate a free strand from array of strands - int16_t strandIdx = allocateStrand(); - if (strandIdx == ERR_INVALID_STRAND) { // unable to allocate a strand - if (pixelsCreateReqMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) - publishAddStrandResponse(false, pixelsCreateReqMsg->pixels_pin_neopixel); - else if (pixelsCreateReqMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) - publishAddStrandResponse(false, - pixelsCreateReqMsg->pixels_pin_dotstar_data); - return false; - } - - // fill generic members of the strand obj. - strands[strandIdx].type = pixelsCreateReqMsg->pixels_type; - strands[strandIdx].brightness = pixelsCreateReqMsg->pixels_brightness; - strands[strandIdx].numPixels = pixelsCreateReqMsg->pixels_num; - strands[strandIdx].ordering = pixelsCreateReqMsg->pixels_ordering; - - // fill strand pins - if (pixelsCreateReqMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) { - strands[strandIdx].pinNeoPixel = - atoi(pixelsCreateReqMsg->pixels_pin_neopixel + 1); - } else if (pixelsCreateReqMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) { - strands[strandIdx].pinDotStarData = - atoi(pixelsCreateReqMsg->pixels_pin_dotstar_data + 1); - strands[strandIdx].pinDotStarClock = - atoi(pixelsCreateReqMsg->pixels_pin_dotstar_clock + 1); - } else { - WS_DEBUG_PRINTLN("ERROR: Invalid strand type provided!"); - publishAddStrandResponse(false, pixelsCreateReqMsg->pixels_pin_neopixel); - return false; - } - - // Fill specific members of strand obj. - if (pixelsCreateReqMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) { - // Release status LED - // is requested pin in-use by the status pixel? - if (getStatusNeoPixelPin() == strands[strandIdx].pinNeoPixel && - WS.lockStatusNeoPixel) - releaseStatusLED(); // release it! - - // Create a new strand of NeoPixels - strands[strandIdx].neoPixelPtr = new Adafruit_NeoPixel( - pixelsCreateReqMsg->pixels_num, strands[strandIdx].pinNeoPixel, - getNeoPixelStrandOrder(pixelsCreateReqMsg->pixels_ordering)); - - // Initialize strand - strands[strandIdx].neoPixelPtr->begin(); - strands[strandIdx].neoPixelPtr->setBrightness( - strands[strandIdx].brightness); - strands[strandIdx].neoPixelPtr->clear(); - strands[strandIdx].neoPixelPtr->show(); - - // Check that we've correctly initialized the strand - if (strands[strandIdx].neoPixelPtr->numPixels() == 0) { - publishAddStrandResponse(false, pixelsCreateReqMsg->pixels_pin_neopixel); - return false; - } - - WS_DEBUG_PRINT("Created NeoPixel strand of length "); - WS_DEBUG_PRINT(pixelsCreateReqMsg->pixels_num); - WS_DEBUG_PRINT(" on GPIO #"); - WS_DEBUG_PRINTLN(pixelsCreateReqMsg->pixels_pin_neopixel); - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[Pixel] Added NeoPixel strand on Pin %s\n.", - pixelsCreateReqMsg->pixels_pin_neopixel); - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - publishAddStrandResponse(true, pixelsCreateReqMsg->pixels_pin_neopixel); - } else if (pixelsCreateReqMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) { - - // release the status dotstar, if it is both in-use and the pin within - // `pixelsCreateReqMsg` - if ((strands[strandIdx].pinDotStarData == getStatusDotStarDataPin()) && - WS.lockStatusDotStar) { - releaseStatusLED(); - } - - // Create Dotstar strand - strands[strandIdx].dotStarPtr = new Adafruit_DotStar( - strands[strandIdx].numPixels, strands[strandIdx].pinDotStarData, - strands[strandIdx].pinDotStarClock, - getDotStarStrandOrder(strands[strandIdx].ordering)); - - // initialize strand - strands[strandIdx].dotStarPtr->begin(); - strands[strandIdx].dotStarPtr->setBrightness(strands[strandIdx].brightness); - strands[strandIdx].dotStarPtr->clear(); - strands[strandIdx].dotStarPtr->show(); - - // post-init sanity check - if (strands[strandIdx].dotStarPtr->numPixels() == 0) { - publishAddStrandResponse(false, - pixelsCreateReqMsg->pixels_pin_dotstar_data); - return false; - } - - WS_DEBUG_PRINT("Created DotStar strand of length "); - WS_DEBUG_PRINT(strands[strandIdx].numPixels); - WS_DEBUG_PRINT(" on Data GPIO #"); - WS_DEBUG_PRINTLN(strands[strandIdx].pinDotStarData); - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[Pixel] Added NeoPixel strand on Pin %s\n.", - pixelsCreateReqMsg->pixels_pin_neopixel); - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - publishAddStrandResponse(true, pixelsCreateReqMsg->pixels_pin_dotstar_data); - } else { - WS_DEBUG_PRINTLN("ERROR: Invalid strand type provided!"); - publishAddStrandResponse(false, - pixelsCreateReqMsg->pixels_pin_dotstar_data); - return false; - } - - return true; -} - -/**************************************************************************/ -/*! - @brief Obtains the index of a `strand_t` within array of `strands`. - @param dataPin - strand_t's data dataPin - @param type - Type of strand_t, NeoPixel or DotStar. - @returns The index of a strand_t if within strands[], - ERR_INVALID_STRAND otherwise. -*/ -/**************************************************************************/ -int ws_pixels::getStrandIdx(int16_t dataPin, - wippersnapper_pixels_v1_PixelsType type) { - for (size_t strandIdx = 0; strandIdx < sizeof(strands) / sizeof(strands[0]); - strandIdx++) { - if (type == wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL && - strands[strandIdx].pinNeoPixel == dataPin) - return strandIdx; - if (type == wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR && - strands[strandIdx].pinDotStarData == dataPin) - return strandIdx; - } - return ERR_INVALID_STRAND; -} - -/**************************************************************************/ -/*! - @brief Deletes a `strand_t` from `strands`, deinitializes a strand, - and frees its resources. - @param pixelsDeleteMsg - Protobuf message from Adafruit IO containing a - `wippersnapper_pixels_v1_PixelsDeleteRequest`. -*/ -/**************************************************************************/ -void ws_pixels::deleteStrand( - wippersnapper_pixels_v1_PixelsDeleteRequest *pixelsDeleteMsg) { - int strandIdx = getStrandIdx(atoi(pixelsDeleteMsg->pixels_pin_data + 1), - pixelsDeleteMsg->pixels_type); - if (strandIdx == ERR_INVALID_STRAND) { - WS_DEBUG_PRINTLN("ERROR: Strand not found, unable to delete strand!"); - return; - } - // deallocate and release resources of strand object - deallocateStrand(strandIdx); - - WS_DEBUG_PRINT("Deleted strand on data pin "); - WS_DEBUG_PRINTLN(pixelsDeleteMsg->pixels_pin_data); - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[Pixel] Deleted strand on pin %s\n.", - pixelsDeleteMsg->pixels_pin_data); - WS._ui_helper->add_text_to_terminal(buffer); -#endif -} - -/**************************************************************************/ -/*! - @brief Gets the gamma-corrected color, provided a pixel_color - @param pixel_color - Strand's color from Adafruit IO. - @param strand - Desired strand struct. to access. - @returns A gamma-corrected strand color -*/ -/**************************************************************************/ -uint32_t ws_pixels::getGammaCorrectedColor(uint32_t pixel_color, - strand_s strand) { - if (strand.neoPixelPtr != nullptr) { - return strand.neoPixelPtr->gamma32(pixel_color); - } else if (strand.dotStarPtr != nullptr) { - return strand.dotStarPtr->gamma32(pixel_color); - } else { - WS_DEBUG_PRINTLN( - "ERROR: Unable to perform gamma correction, unknown strand type!"); - return 0; - } -} - -/**************************************************************************/ -/*! - @brief Writes a color from Adafruit IO to a strand of - addressable pixels - @param pixelsWriteMsg - Protobuf message from Adafruit IO containing a - `wippersnapper_pixels_v1_PixelsWriteRequest`. -*/ -/**************************************************************************/ -void ws_pixels::fillStrand( - wippersnapper_pixels_v1_PixelsWriteRequest *pixelsWriteMsg) { - - // Get index of pixel strand - int strandIdx = getStrandIdx(atoi(pixelsWriteMsg->pixels_pin_data + 1), - pixelsWriteMsg->pixels_type); - if (strandIdx == ERR_INVALID_STRAND) { - WS_DEBUG_PRINTLN( - "ERROR: Pixel strand not found, can not write a color to the strand!"); - return; - } - - uint32_t rgbColorGamma = - getGammaCorrectedColor(pixelsWriteMsg->pixels_color, strands[strandIdx]); - - WS_DEBUG_PRINT("Filling color: "); - WS_DEBUG_PRINTLN(pixelsWriteMsg->pixels_color); - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[Pixel] Filling strand on pin %s with color %u\n", - pixelsWriteMsg->pixels_pin_data, - (unsigned int)pixelsWriteMsg->pixels_color); - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - if (pixelsWriteMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) { - strands[strandIdx].neoPixelPtr->fill(rgbColorGamma); - strands[strandIdx].neoPixelPtr->show(); - } else if (pixelsWriteMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) { - strands[strandIdx].dotStarPtr->fill(rgbColorGamma); - strands[strandIdx].dotStarPtr->show(); - } else { - WS_DEBUG_PRINTLN("ERROR: Unable to determine pixel type to write to!"); - } +/*! + * @file ws_pixels.cpp + * + * High-level interface for wippersnapper to manage addressable RGB pixel + * strands + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * + * Written by Brent Rubell for Adafruit Industries, 2022-2023 + * + * MIT license, all text here must be included in any redistribution. + * + */ +#include "ws_pixels.h" + +strand_s strands[MAX_PIXEL_STRANDS]{ + nullptr, + nullptr, + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_UNSPECIFIED, + 0, + 0, + wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_UNSPECIFIED, + -1, + -1, + -1}; ///< Contains all pixel strands used by WipperSnapper + +/**************************************************************************/ +/*! + @brief Destructor +*/ +/**************************************************************************/ +ws_pixels::~ws_pixels() { + // de-allocate all strands + for (size_t i = 0; i < sizeof(strands) / sizeof(strands[0]); i++) + deallocateStrand(i); +} + +/******************************************************************************/ +/*! + @brief Allocates an index of a free strand_t within the strand array. + @returns Index of a free strand_t, ERR_INVALID_STRAND if strand array is + full. +*/ +/******************************************************************************/ +int16_t ws_pixels::allocateStrand() { + for (size_t strandIdx = 0; strandIdx < sizeof(strands) / sizeof(strands[0]); + strandIdx++) { + if (strands[strandIdx].type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_UNSPECIFIED) { + return strandIdx; + } + } + return ERR_INVALID_STRAND; +} + +/**************************************************************************/ +/*! + @brief Deallocates a `strand_t` within `strands`, provided an index. + @param strandIdx + The desired index of a `strand_t` within `strands`. +*/ +/**************************************************************************/ +void ws_pixels::deallocateStrand(int16_t strandIdx) { + + // delete the pixel object + if (strands[strandIdx].neoPixelPtr != nullptr) + delete strands[strandIdx].neoPixelPtr; + if ((strands[strandIdx].dotStarPtr != nullptr)) + delete strands[strandIdx].dotStarPtr; + + // re-initialize status pixel (if pixel was prvsly used) + if (strands[strandIdx].pinNeoPixel == getStatusNeoPixelPin() || + strands[strandIdx].pinDotStarData == getStatusDotStarDataPin()) { + initStatusLED(); + } + + // reset the strand + strands[strandIdx] = { + nullptr, + nullptr, + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_UNSPECIFIED, + 0, + 0, + wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_UNSPECIFIED, + -1, + -1, + -1}; +} + +/**************************************************************************/ +/*! + @brief Returns the `neoPixelType` provided the strand's pixelOrder + @param pixelOrder + Desired pixel order, from init. message. + @returns Type of NeoPixel strand, usable by Adafruit_NeoPixel + constructor +*/ +/**************************************************************************/ +neoPixelType ws_pixels::getNeoPixelStrandOrder( + wippersnapper_pixels_v1_PixelsOrder pixelOrder) { + switch (pixelOrder) { + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GRB: + return NEO_GRB + NEO_KHZ800; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GRBW: + return NEO_GRBW + NEO_KHZ800; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RGB: + return NEO_RGB + NEO_KHZ800; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RGBW: + return NEO_RGBW + NEO_KHZ800; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_BRG: + return NEO_BRG + NEO_KHZ800; + default: + return NEO_GRB + NEO_KHZ800; + } +} + +/**************************************************************************/ +/*! + @brief Returns the color order of the DotStar strand. + @param pixelOrder + Desired pixel order, from init. message. + @returns Type of DotStar strand. +*/ +/**************************************************************************/ +uint8_t ws_pixels::getDotStarStrandOrder( + wippersnapper_pixels_v1_PixelsOrder pixelOrder) { + switch (pixelOrder) { + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GRB: + return DOTSTAR_GRB; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RGB: + return DOTSTAR_RGB; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_BRG: + return DOTSTAR_BRG; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RBG: + return DOTSTAR_RBG; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GBR: + return DOTSTAR_GBR; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_BGR: + return DOTSTAR_BGR; + default: + return DOTSTAR_BRG; + } +} + +/**************************************************************************/ +/*! + @brief Creates a PixelsResponse message and publishes it to IO. + @param is_success + True if `addStrand()` succeeded, False otherwise. + @param pixels_pin_data + The strand's data pin.. +*/ +/**************************************************************************/ +void ws_pixels::publishAddStrandResponse(bool is_success, + char *pixels_pin_data) { + // Create response message + wippersnapper_signal_v1_PixelsResponse msgInitResp = + wippersnapper_signal_v1_PixelsResponse_init_zero; + msgInitResp.which_payload = + wippersnapper_signal_v1_PixelsResponse_resp_pixels_create_tag; + // Fill response message + msgInitResp.payload.resp_pixels_create.is_success = is_success; + memcpy(msgInitResp.payload.resp_pixels_create.pixels_pin_data, + pixels_pin_data, sizeof(char) * 6); + + // Encode `wippersnapper_pixels_v1_PixelsCreateResponse` message + 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 (!ws_pb_encode(&ostream, wippersnapper_signal_v1_PixelsResponse_fields, + &msgInitResp)) { + WS_DEBUG_PRINTLN("ERROR: Unable to encode " + "wippersnapper_signal_v1_PixelsResponse message!"); + return; + } + + // Publish message to broker + size_t msgSz; + pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_PixelsResponse_fields, + &msgInitResp); + WS_DEBUG_PRINT("-> wippersnapper_signal_v1_PixelsResponse..."); + WS._mqtt->publish(WS._topic_signal_pixels_device, WS._buffer_outgoing, msgSz, + 1); + WS_DEBUG_PRINTLN("Published!"); +} + +/**************************************************************************/ +/*! + @brief Initializes a strand of addressable RGB Pixels. + @param pixelsCreateReqMsg + Pointer to strand init. request message + @returns True if successfully initialized, False otherwise. +*/ +/**************************************************************************/ +bool ws_pixels::addStrand( + wippersnapper_pixels_v1_PixelsCreateRequest *pixelsCreateReqMsg) { + // attempt to allocate a free strand from array of strands + int16_t strandIdx = allocateStrand(); + if (strandIdx == ERR_INVALID_STRAND) { // unable to allocate a strand + if (pixelsCreateReqMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) + publishAddStrandResponse(false, pixelsCreateReqMsg->pixels_pin_neopixel); + else if (pixelsCreateReqMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) + publishAddStrandResponse(false, + pixelsCreateReqMsg->pixels_pin_dotstar_data); + return false; + } + + // fill generic members of the strand obj. + strands[strandIdx].type = pixelsCreateReqMsg->pixels_type; + strands[strandIdx].brightness = pixelsCreateReqMsg->pixels_brightness; + strands[strandIdx].numPixels = pixelsCreateReqMsg->pixels_num; + strands[strandIdx].ordering = pixelsCreateReqMsg->pixels_ordering; + + // fill strand pins + if (pixelsCreateReqMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) { + strands[strandIdx].pinNeoPixel = + atoi(pixelsCreateReqMsg->pixels_pin_neopixel + 1); + } else if (pixelsCreateReqMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) { + strands[strandIdx].pinDotStarData = + atoi(pixelsCreateReqMsg->pixels_pin_dotstar_data + 1); + strands[strandIdx].pinDotStarClock = + atoi(pixelsCreateReqMsg->pixels_pin_dotstar_clock + 1); + } else { + WS_DEBUG_PRINTLN("ERROR: Invalid strand type provided!"); + publishAddStrandResponse(false, pixelsCreateReqMsg->pixels_pin_neopixel); + return false; + } + + // Fill specific members of strand obj. + if (pixelsCreateReqMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) { + // Release status LED + // is requested pin in-use by the status pixel? + if (getStatusNeoPixelPin() == strands[strandIdx].pinNeoPixel && + WS.lockStatusNeoPixel) + releaseStatusLED(); // release it! + + // Create a new strand of NeoPixels + strands[strandIdx].neoPixelPtr = new Adafruit_NeoPixel( + pixelsCreateReqMsg->pixels_num, strands[strandIdx].pinNeoPixel, + getNeoPixelStrandOrder(pixelsCreateReqMsg->pixels_ordering)); + + // Initialize strand + strands[strandIdx].neoPixelPtr->begin(); + strands[strandIdx].neoPixelPtr->setBrightness( + strands[strandIdx].brightness); + strands[strandIdx].neoPixelPtr->clear(); + strands[strandIdx].neoPixelPtr->show(); + + // Check that we've correctly initialized the strand + if (strands[strandIdx].neoPixelPtr->numPixels() == 0) { + publishAddStrandResponse(false, pixelsCreateReqMsg->pixels_pin_neopixel); + return false; + } + + WS_DEBUG_PRINT("Created NeoPixel strand of length "); + WS_DEBUG_PRINT(pixelsCreateReqMsg->pixels_num); + WS_DEBUG_PRINT(" on GPIO #"); + WS_DEBUG_PRINTLN(pixelsCreateReqMsg->pixels_pin_neopixel); + +#ifdef USE_DISPLAY + char buffer[100]; + snprintf(buffer, 100, "[Pixel] Added NeoPixel strand on Pin %s\n.", + pixelsCreateReqMsg->pixels_pin_neopixel); + WS._ui_helper->add_text_to_terminal(buffer); +#endif + + publishAddStrandResponse(true, pixelsCreateReqMsg->pixels_pin_neopixel); + } else if (pixelsCreateReqMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) { + + // release the status dotstar, if it is both in-use and the pin within + // `pixelsCreateReqMsg` + if ((strands[strandIdx].pinDotStarData == getStatusDotStarDataPin()) && + WS.lockStatusDotStar) { + releaseStatusLED(); + } + + // Create Dotstar strand + strands[strandIdx].dotStarPtr = new Adafruit_DotStar( + strands[strandIdx].numPixels, strands[strandIdx].pinDotStarData, + strands[strandIdx].pinDotStarClock, + getDotStarStrandOrder(strands[strandIdx].ordering)); + + // initialize strand + strands[strandIdx].dotStarPtr->begin(); + strands[strandIdx].dotStarPtr->setBrightness(strands[strandIdx].brightness); + strands[strandIdx].dotStarPtr->clear(); + strands[strandIdx].dotStarPtr->show(); + + // post-init sanity check + if (strands[strandIdx].dotStarPtr->numPixels() == 0) { + publishAddStrandResponse(false, + pixelsCreateReqMsg->pixels_pin_dotstar_data); + return false; + } + + WS_DEBUG_PRINT("Created DotStar strand of length "); + WS_DEBUG_PRINT(strands[strandIdx].numPixels); + WS_DEBUG_PRINT(" on Data GPIO #"); + WS_DEBUG_PRINTLN(strands[strandIdx].pinDotStarData); + +#ifdef USE_DISPLAY + char buffer[100]; + snprintf(buffer, 100, "[Pixel] Added NeoPixel strand on Pin %s\n.", + pixelsCreateReqMsg->pixels_pin_neopixel); + WS._ui_helper->add_text_to_terminal(buffer); +#endif + + publishAddStrandResponse(true, pixelsCreateReqMsg->pixels_pin_dotstar_data); + } else { + WS_DEBUG_PRINTLN("ERROR: Invalid strand type provided!"); + publishAddStrandResponse(false, + pixelsCreateReqMsg->pixels_pin_dotstar_data); + return false; + } + + return true; +} + +/**************************************************************************/ +/*! + @brief Obtains the index of a `strand_t` within array of `strands`. + @param dataPin + strand_t's data dataPin + @param type + Type of strand_t, NeoPixel or DotStar. + @returns The index of a strand_t if within strands[], + ERR_INVALID_STRAND otherwise. +*/ +/**************************************************************************/ +int ws_pixels::getStrandIdx(int16_t dataPin, + wippersnapper_pixels_v1_PixelsType type) { + for (size_t strandIdx = 0; strandIdx < sizeof(strands) / sizeof(strands[0]); + strandIdx++) { + if (type == wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL && + strands[strandIdx].pinNeoPixel == dataPin) + return strandIdx; + if (type == wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR && + strands[strandIdx].pinDotStarData == dataPin) + return strandIdx; + } + return ERR_INVALID_STRAND; +} + +/**************************************************************************/ +/*! + @brief Deletes a `strand_t` from `strands`, deinitializes a strand, + and frees its resources. + @param pixelsDeleteMsg + Protobuf message from Adafruit IO containing a + `wippersnapper_pixels_v1_PixelsDeleteRequest`. +*/ +/**************************************************************************/ +void ws_pixels::deleteStrand( + wippersnapper_pixels_v1_PixelsDeleteRequest *pixelsDeleteMsg) { + int strandIdx = getStrandIdx(atoi(pixelsDeleteMsg->pixels_pin_data + 1), + pixelsDeleteMsg->pixels_type); + if (strandIdx == ERR_INVALID_STRAND) { + WS_DEBUG_PRINTLN("ERROR: Strand not found, unable to delete strand!"); + return; + } + // deallocate and release resources of strand object + deallocateStrand(strandIdx); + + WS_DEBUG_PRINT("Deleted strand on data pin "); + WS_DEBUG_PRINTLN(pixelsDeleteMsg->pixels_pin_data); + +#ifdef USE_DISPLAY + char buffer[100]; + snprintf(buffer, 100, "[Pixel] Deleted strand on pin %s\n.", + pixelsDeleteMsg->pixels_pin_data); + WS._ui_helper->add_text_to_terminal(buffer); +#endif +} + +/**************************************************************************/ +/*! + @brief Gets the gamma-corrected color, provided a pixel_color + @param pixel_color + Strand's color from Adafruit IO. + @param strand + Desired strand struct. to access. + @returns A gamma-corrected strand color +*/ +/**************************************************************************/ +uint32_t ws_pixels::getGammaCorrectedColor(uint32_t pixel_color, + strand_s strand) { + if (strand.neoPixelPtr != nullptr) { + return strand.neoPixelPtr->gamma32(pixel_color); + } else if (strand.dotStarPtr != nullptr) { + return strand.dotStarPtr->gamma32(pixel_color); + } else { + WS_DEBUG_PRINTLN( + "ERROR: Unable to perform gamma correction, unknown strand type!"); + return 0; + } +} + +/**************************************************************************/ +/*! + @brief Writes a color from Adafruit IO to a strand of + addressable pixels + @param pixelsWriteMsg + Protobuf message from Adafruit IO containing a + `wippersnapper_pixels_v1_PixelsWriteRequest`. +*/ +/**************************************************************************/ +void ws_pixels::fillStrand( + wippersnapper_pixels_v1_PixelsWriteRequest *pixelsWriteMsg) { + + // Get index of pixel strand + int strandIdx = getStrandIdx(atoi(pixelsWriteMsg->pixels_pin_data + 1), + pixelsWriteMsg->pixels_type); + if (strandIdx == ERR_INVALID_STRAND) { + WS_DEBUG_PRINTLN( + "ERROR: Pixel strand not found, can not write a color to the strand!"); + return; + } + + uint32_t rgbColorGamma = + getGammaCorrectedColor(pixelsWriteMsg->pixels_color, strands[strandIdx]); + + WS_DEBUG_PRINT("Filling color: "); + WS_DEBUG_PRINTLN(pixelsWriteMsg->pixels_color); + +#ifdef USE_DISPLAY + char buffer[100]; + snprintf(buffer, 100, "[Pixel] Filling strand on pin %s with color %u\n", + pixelsWriteMsg->pixels_pin_data, + (unsigned int)pixelsWriteMsg->pixels_color); + WS._ui_helper->add_text_to_terminal(buffer); +#endif + + if (pixelsWriteMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) { + strands[strandIdx].neoPixelPtr->fill(rgbColorGamma); + strands[strandIdx].neoPixelPtr->show(); + } else if (pixelsWriteMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) { + strands[strandIdx].dotStarPtr->fill(rgbColorGamma); + strands[strandIdx].dotStarPtr->show(); + } else { + WS_DEBUG_PRINTLN("ERROR: Unable to determine pixel type to write to!"); + } } \ No newline at end of file diff --git a/src/components/register/Wippersnapper_Register.cpp b/src/components/register/Wippersnapper_Register.cpp index 071927449..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; @@ -141,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..543761601 100644 --- a/src/components/uart/drivers/ws_uart_drv_pm25aqi.h +++ b/src/components/uart/drivers/ws_uart_drv_pm25aqi.h @@ -1,215 +1,215 @@ -/*! - * @file ws_uart_drv_pm25aqi.h - * - * WipperSnapper device driver for the Adafruit_PM25AQI Arduino Library - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2023 for Adafruit Industries. - * - * MIT license, all text here must be included in any redistribution. - * - */ -#ifndef WS_UART_DRV_PM25AQI_H -#define WS_UART_DRV_PM25AQI_H - -#include "Wippersnapper.h" -#include "ws_uart_drv.h" -#include - -/**************************************************************************/ -/*! - @brief Class that provides an interface for a PM25 AQI UART sensor. -*/ -/**************************************************************************/ -class ws_uart_drv_pm25aqi : public ws_uart_drv { -public: -#ifdef USE_SW_UART - /*******************************************************************************/ - /*! - @brief Initializes a PM25AQI UART device driver. - @param swSerial - Pointer to an instance of a SoftwareSerial object. - @param pollingInterval - How often the PM25AQI device will be polled, in milliseconds. - */ - /*******************************************************************************/ - ws_uart_drv_pm25aqi(SoftwareSerial *swSerial, int32_t interval) - : ws_uart_drv(swSerial, interval) { - _swSerial = swSerial; - pollingInterval = (unsigned long)interval; - }; -#else - /*******************************************************************************/ - /*! - @brief Initializes the PM25AQI UART device driver. - @param hwSerial - Pointer to an instance of a HardwareSerial object. - @param interval - How often the PM25AQI device will be polled, in milliseconds. - */ - /*******************************************************************************/ - ws_uart_drv_pm25aqi(HardwareSerial *hwSerial, int32_t interval) - : ws_uart_drv(hwSerial, interval) { - _hwSerial = hwSerial; - pollingInterval = (unsigned long)interval; - }; -#endif // USE_SW_UART - - /*******************************************************************************/ - /*! - @brief Destructor for a PM25AQI sensor. - */ - /*******************************************************************************/ - ~ws_uart_drv_pm25aqi() { - delete _aqi; -#ifdef USE_SW_UART - _swSerial = nullptr; -#else - _hwSerial = nullptr; -#endif - } - - /*******************************************************************************/ - /*! - @brief Initializes a PM25AQI sensor. - @returns True if the PM25AQI sensor was successfully initialized, - False otherwise. - */ - /*******************************************************************************/ - bool begin() override { - _aqi = new Adafruit_PM25AQI(); -#ifdef USE_SW_UART - if (!_aqi->begin_UART( - _swSerial)) { // connect to the sensor over software serial - return false; - } -#else - if (!_aqi->begin_UART( - _hwSerial)) { // connect to the sensor over hardware serial - return false; - } -#endif - return true; - } - - /*******************************************************************************/ - /*! - @brief Attempts to read data from the PM25AQI sensor. - @returns True if data was successfully read, False otherwise. - */ - /*******************************************************************************/ - bool read_data() override { - Serial.println("[UART, PM25] Reading data..."); - // Attempt to read the PM2.5 Sensor - if (!_aqi->read(&_data)) { - Serial.println("[UART, PM25] Data not available."); - delay(500); - return false; - } - Serial.println("[UART, PM25] Read data OK"); - Serial.println(); - Serial.println(F("---------------------------------------")); - Serial.println(F("Concentration Units (standard)")); - Serial.println(F("---------------------------------------")); - Serial.print(F("PM 1.0: ")); - Serial.print(_data.pm10_standard); - Serial.print(F("\t\tPM 2.5: ")); - Serial.print(_data.pm25_standard); - Serial.print(F("\t\tPM 10: ")); - Serial.println(_data.pm100_standard); - Serial.println(F("Concentration Units (environmental)")); - Serial.println(F("---------------------------------------")); - Serial.print(F("PM 1.0: ")); - Serial.print(_data.pm10_env); - Serial.print(F("\t\tPM 2.5: ")); - Serial.print(_data.pm25_env); - Serial.print(F("\t\tPM 10: ")); - Serial.println(_data.pm100_env); - Serial.println(F("---------------------------------------")); - - return true; - } - - /*******************************************************************************/ - /*! - @brief Packs and sends the device's event data to Adafruit IO. - */ - /*******************************************************************************/ - void send_data() override { - // Create a new UART response message - wippersnapper_signal_v1_UARTResponse msgUARTResponse = - wippersnapper_signal_v1_UARTResponse_init_zero; - msgUARTResponse.which_payload = - wippersnapper_signal_v1_UARTResponse_resp_uart_device_event_tag; - strcpy(msgUARTResponse.payload.resp_uart_device_event.device_id, - getDriverID()); - - // check if driverID is pm1006 - if (strcmp(getDriverID(), "pm1006") == 0) { - // PM1006 returns only PM2.5_ENV readings - msgUARTResponse.payload.resp_uart_device_event.sensor_event_count = 1; - packUARTResponse(&msgUARTResponse, 0, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_ENV, - (float)_data.pm25_env); - } else { - msgUARTResponse.payload.resp_uart_device_event.sensor_event_count = 6; - packUARTResponse(&msgUARTResponse, 0, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM10_STD, - (float)_data.pm10_standard); - - packUARTResponse(&msgUARTResponse, 1, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_STD, - (float)_data.pm25_standard); - - packUARTResponse(&msgUARTResponse, 2, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM100_STD, - (float)_data.pm100_standard); - - packUARTResponse(&msgUARTResponse, 3, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM10_ENV, - (float)_data.pm10_env); - - packUARTResponse(&msgUARTResponse, 4, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_ENV, - (float)_data.pm25_env); - - packUARTResponse(&msgUARTResponse, 5, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM100_ENV, - (float)_data.pm100_env); - } - - // Encode message data - 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)) { - Serial.println("[ERROR, UART]: Unable to encode device response!"); - return; - } - - // Publish message to IO - size_t msgSz; - pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_UARTResponse_fields, - &msgUARTResponse); - Serial.print("[UART] Publishing event to IO.."); - mqttClient->publish(uartTopic, mqttBuffer, msgSz, 1); - Serial.println("Published!"); - - setPrvPollTime(millis()); - } - -protected: - Adafruit_PM25AQI *_aqi = nullptr; ///< Pointer to PM25AQI sensor object - PM25_AQI_Data _data; ///< PM25AQI sensor data struct. -#ifdef USE_SW_UART - SoftwareSerial *_swSerial = nullptr; ///< Pointer to Software UART interface -#else - HardwareSerial *_hwSerial = nullptr; ///< Pointer to Hardware UART interface -#endif -}; - +/*! + * @file ws_uart_drv_pm25aqi.h + * + * WipperSnapper device driver for the Adafruit_PM25AQI Arduino Library + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2023 for Adafruit Industries. + * + * MIT license, all text here must be included in any redistribution. + * + */ +#ifndef WS_UART_DRV_PM25AQI_H +#define WS_UART_DRV_PM25AQI_H + +#include "Wippersnapper.h" +#include "ws_uart_drv.h" +#include + +/**************************************************************************/ +/*! + @brief Class that provides an interface for a PM25 AQI UART sensor. +*/ +/**************************************************************************/ +class ws_uart_drv_pm25aqi : public ws_uart_drv { +public: +#ifdef USE_SW_UART + /*******************************************************************************/ + /*! + @brief Initializes a PM25AQI UART device driver. + @param swSerial + Pointer to an instance of a SoftwareSerial object. + @param pollingInterval + How often the PM25AQI device will be polled, in milliseconds. + */ + /*******************************************************************************/ + ws_uart_drv_pm25aqi(SoftwareSerial *swSerial, int32_t interval) + : ws_uart_drv(swSerial, interval) { + _swSerial = swSerial; + pollingInterval = (unsigned long)interval; + }; +#else + /*******************************************************************************/ + /*! + @brief Initializes the PM25AQI UART device driver. + @param hwSerial + Pointer to an instance of a HardwareSerial object. + @param interval + How often the PM25AQI device will be polled, in milliseconds. + */ + /*******************************************************************************/ + ws_uart_drv_pm25aqi(HardwareSerial *hwSerial, int32_t interval) + : ws_uart_drv(hwSerial, interval) { + _hwSerial = hwSerial; + pollingInterval = (unsigned long)interval; + }; +#endif // USE_SW_UART + + /*******************************************************************************/ + /*! + @brief Destructor for a PM25AQI sensor. + */ + /*******************************************************************************/ + ~ws_uart_drv_pm25aqi() { + delete _aqi; +#ifdef USE_SW_UART + _swSerial = nullptr; +#else + _hwSerial = nullptr; +#endif + } + + /*******************************************************************************/ + /*! + @brief Initializes a PM25AQI sensor. + @returns True if the PM25AQI sensor was successfully initialized, + False otherwise. + */ + /*******************************************************************************/ + bool begin() override { + _aqi = new Adafruit_PM25AQI(); +#ifdef USE_SW_UART + if (!_aqi->begin_UART( + _swSerial)) { // connect to the sensor over software serial + return false; + } +#else + if (!_aqi->begin_UART( + _hwSerial)) { // connect to the sensor over hardware serial + return false; + } +#endif + return true; + } + + /*******************************************************************************/ + /*! + @brief Attempts to read data from the PM25AQI sensor. + @returns True if data was successfully read, False otherwise. + */ + /*******************************************************************************/ + bool read_data() override { + Serial.println("[UART, PM25] Reading data..."); + // Attempt to read the PM2.5 Sensor + if (!_aqi->read(&_data)) { + Serial.println("[UART, PM25] Data not available."); + delay(500); + return false; + } + Serial.println("[UART, PM25] Read data OK"); + Serial.println(); + Serial.println(F("---------------------------------------")); + Serial.println(F("Concentration Units (standard)")); + Serial.println(F("---------------------------------------")); + Serial.print(F("PM 1.0: ")); + Serial.print(_data.pm10_standard); + Serial.print(F("\t\tPM 2.5: ")); + Serial.print(_data.pm25_standard); + Serial.print(F("\t\tPM 10: ")); + Serial.println(_data.pm100_standard); + Serial.println(F("Concentration Units (environmental)")); + Serial.println(F("---------------------------------------")); + Serial.print(F("PM 1.0: ")); + Serial.print(_data.pm10_env); + Serial.print(F("\t\tPM 2.5: ")); + Serial.print(_data.pm25_env); + Serial.print(F("\t\tPM 10: ")); + Serial.println(_data.pm100_env); + Serial.println(F("---------------------------------------")); + + return true; + } + + /*******************************************************************************/ + /*! + @brief Packs and sends the device's event data to Adafruit IO. + */ + /*******************************************************************************/ + void send_data() override { + // Create a new UART response message + wippersnapper_signal_v1_UARTResponse msgUARTResponse = + wippersnapper_signal_v1_UARTResponse_init_zero; + msgUARTResponse.which_payload = + wippersnapper_signal_v1_UARTResponse_resp_uart_device_event_tag; + strcpy(msgUARTResponse.payload.resp_uart_device_event.device_id, + getDriverID()); + + // check if driverID is pm1006 + if (strcmp(getDriverID(), "pm1006") == 0) { + // PM1006 returns only PM2.5_ENV readings + msgUARTResponse.payload.resp_uart_device_event.sensor_event_count = 1; + packUARTResponse(&msgUARTResponse, 0, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_ENV, + (float)_data.pm25_env); + } else { + msgUARTResponse.payload.resp_uart_device_event.sensor_event_count = 6; + packUARTResponse(&msgUARTResponse, 0, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM10_STD, + (float)_data.pm10_standard); + + packUARTResponse(&msgUARTResponse, 1, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_STD, + (float)_data.pm25_standard); + + packUARTResponse(&msgUARTResponse, 2, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM100_STD, + (float)_data.pm100_standard); + + packUARTResponse(&msgUARTResponse, 3, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM10_ENV, + (float)_data.pm10_env); + + packUARTResponse(&msgUARTResponse, 4, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_ENV, + (float)_data.pm25_env); + + packUARTResponse(&msgUARTResponse, 5, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM100_ENV, + (float)_data.pm100_env); + } + + // Encode message data + uint8_t mqttBuffer[512] = {0}; + pb_ostream_t ostream = + pb_ostream_from_buffer(mqttBuffer, sizeof(mqttBuffer)); + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_UARTResponse_fields, + &msgUARTResponse)) { + Serial.println("[ERROR, UART]: Unable to encode device response!"); + return; + } + + // Publish message to IO + size_t msgSz; + pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_UARTResponse_fields, + &msgUARTResponse); + Serial.print("[UART] Publishing event to IO.."); + mqttClient->publish(uartTopic, mqttBuffer, msgSz, 1); + Serial.println("Published!"); + + setPrvPollTime(millis()); + } + +protected: + Adafruit_PM25AQI *_aqi = nullptr; ///< Pointer to PM25AQI sensor object + PM25_AQI_Data _data; ///< PM25AQI sensor data struct. +#ifdef USE_SW_UART + SoftwareSerial *_swSerial = nullptr; ///< Pointer to Software UART interface +#else + HardwareSerial *_hwSerial = nullptr; ///< Pointer to Hardware UART interface +#endif +}; + #endif // WS_UART_DRV_PM25AQI_H \ No newline at end of file From 090862103cd067ae0bcf80a3f11f49e1c1a0d1c8 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Thu, 9 May 2024 18:58:41 +0100 Subject: [PATCH 06/54] set lib_archive = no for samd --- platformio.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 74b32c718..951a6b57b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -99,7 +99,7 @@ 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 @@ -115,6 +115,9 @@ build_flags = -DUSE_TINYUSB lib_ignore = WiFiNINA, 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 From 7393a97e77880c4475cd76423c0180b7160d6d89 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Fri, 10 May 2024 14:08:41 +0100 Subject: [PATCH 07/54] remove static --- src/nanopb/ws_pb_helpers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nanopb/ws_pb_helpers.h b/src/nanopb/ws_pb_helpers.h index 79cbf9807..20c2bf960 100644 --- a/src/nanopb/ws_pb_helpers.h +++ b/src/nanopb/ws_pb_helpers.h @@ -33,7 +33,7 @@ @return True if decode was successful, false otherwise. !*/ // ***************************************************************************** -static bool ws_pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, +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) { @@ -55,7 +55,7 @@ static bool ws_pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, @return True if encode was successful, false otherwise. !*/ // ***************************************************************************** -static bool ws_pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, +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) { From 17db34cca9c19f8a1e33ed216cfa495f8ad8c228 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Fri, 10 May 2024 14:20:05 +0100 Subject: [PATCH 08/54] refactor header file --- src/nanopb/ws_pb_helpers.cpp | 64 ++++++++++++++++++++++++++++++++++++ src/nanopb/ws_pb_helpers.h | 48 ++------------------------- 2 files changed, 67 insertions(+), 45 deletions(-) create mode 100644 src/nanopb/ws_pb_helpers.cpp diff --git a/src/nanopb/ws_pb_helpers.cpp b/src/nanopb/ws_pb_helpers.cpp new file mode 100644 index 000000000..a8e49d9c8 --- /dev/null +++ b/src/nanopb/ws_pb_helpers.cpp @@ -0,0 +1,64 @@ +/*! + * @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. + * + */ +#include "pb.h" +#include "pb_decode.h" +#include "pb_encode.h" +#include "ws_pb_helpers.h" +#include + + +// ***************************************************************************** +/*! + @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 index 20c2bf960..67ef98467 100644 --- a/src/nanopb/ws_pb_helpers.h +++ b/src/nanopb/ws_pb_helpers.h @@ -16,53 +16,11 @@ #define WS_PB_ENCODE_H #include "pb.h" -#include "pb_decode.h" -#include "pb_encode.h" -#include - -// ***************************************************************************** -/*! - @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; -} + void *dest_struct); -// ***************************************************************************** -/*! - @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; -} - + const void *src_struct); + #endif // WS_PB_ENCODE_H \ No newline at end of file From b8a42a4d6adc3aabc98bd5e39ae7b16ec51236ff Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Tue, 21 May 2024 20:43:22 +0100 Subject: [PATCH 09/54] Fix: Log if ping is successful or not --- src/Wippersnapper.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 88c2f6170..12f58f576 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -2538,9 +2538,13 @@ 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!"); + WS_DEBUG_PRINTLN("Sending PING: "); // TODO: Add back, is crashing currently - WS._mqtt->ping(); + if (WS._mqtt->ping()){ + WS_DEBUG_PRINTLN("SUCCESS!"); + } else { + WS_DEBUG_PRINTLN("FAILURE!"); + } _prv_ping = millis(); } // blink status LED every STATUS_LED_KAT_BLINK_TIME millis From 1f81fe296eaa3ae02d87139f33622fc4a62578f6 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Tue, 21 May 2024 22:13:18 +0100 Subject: [PATCH 10/54] Add logging if MQTT publishes fail --- src/Wippersnapper.cpp | 26 +++++++++++++++++------- src/components/ds18x20/ws_ds18x20.cpp | 2 ++ src/components/i2c/WipperSnapper_I2C.cpp | 1 + 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 12f58f576..0e61fa910 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -547,8 +547,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!"); + } } /******************************************************************************************/ @@ -1146,8 +1150,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 @@ -1553,8 +1560,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 == @@ -2616,7 +2626,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!"); + } } /**************************************************************/ diff --git a/src/components/ds18x20/ws_ds18x20.cpp b/src/components/ds18x20/ws_ds18x20.cpp index 7c9d4561d..39c7e38df 100644 --- a/src/components/ds18x20/ws_ds18x20.cpp +++ b/src/components/ds18x20/ws_ds18x20.cpp @@ -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 927a5b1cd..699e0f1ed 100644 --- a/src/components/i2c/WipperSnapper_I2C.cpp +++ b/src/components/i2c/WipperSnapper_I2C.cpp @@ -862,6 +862,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!"); From 5a6931bd7de22612042a3e54feb2b6c19edda2a3 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Tue, 21 May 2024 22:38:18 +0100 Subject: [PATCH 11/54] Clang tidy ping --- src/Wippersnapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 0e61fa910..14888ec0f 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -2550,7 +2550,7 @@ void Wippersnapper::pingBroker() { (WS_KEEPALIVE_INTERVAL_MS * 0.10)))) { WS_DEBUG_PRINTLN("Sending PING: "); // TODO: Add back, is crashing currently - if (WS._mqtt->ping()){ + if (WS._mqtt->ping()) { WS_DEBUG_PRINTLN("SUCCESS!"); } else { WS_DEBUG_PRINTLN("FAILURE!"); From fdb2ef2be4c5fe23f94d34df0ea25743c5d8b0a6 Mon Sep 17 00:00:00 2001 From: tyeth Date: Wed, 22 May 2024 01:38:32 +0100 Subject: [PATCH 12/54] Correct publish check for i2c --- src/Wippersnapper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 14888ec0f..9ba48e3ac 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -547,8 +547,8 @@ void publishI2CResponse(wippersnapper_signal_v1_I2CResponse *msgi2cResponse) { pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_I2CResponse_fields, msgi2cResponse); WS_DEBUG_PRINT("Publishing Message: I2CResponse..."); - if (WS._mqtt->publish(WS._topic_signal_i2c_device, WS._buffer_outgoing, msgSz, - 1)) { + 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!"); From f17e5bea388c3c2252823877b0dc5c83876395c8 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Wed, 22 May 2024 16:32:27 +0100 Subject: [PATCH 13/54] Fix: set ping logging to be one line --- src/Wippersnapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 9ba48e3ac..a94a0943d 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -2548,7 +2548,7 @@ 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("Sending PING: "); + WS_DEBUG_PRINT("Sending PING: "); // TODO: Add back, is crashing currently if (WS._mqtt->ping()) { WS_DEBUG_PRINTLN("SUCCESS!"); From aeef7d10fb89e32038db9bd288b1d4163f6b6541 Mon Sep 17 00:00:00 2001 From: tyeth Date: Wed, 29 May 2024 16:06:15 +0100 Subject: [PATCH 14/54] Add reset before connect for AIRLIFT/WIFININA --- .../Wippersnapper_AIRLIFT.h | 582 +++++++++--------- .../Wippersnapper_WIFININA.h | 564 ++++++++--------- 2 files changed, 588 insertions(+), 558 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index ce39590b9..dbd8bc2f2 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -1,285 +1,299 @@ -/*! - * @file Wippersnapper_AIRLIFT.h - * - * This is a driver for using the Adafruit AirLift - * ESP32 Co-Processor's network interface with Wippersnapper. - * - * The ESP32 AirLift uses SPI to communicate. Three lines (CS, ACK, RST) are - * required to communicate with the ESP32 AirLift. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries. - * - * MIT license, all text here must be included in any redistribution. - * - */ - -#ifndef WIPPERSNAPPER_AIRLIFT_H -#define WIPPERSNAPPER_AIRLIFT_H - -#include "Adafruit_MQTT.h" -#include "Adafruit_MQTT_Client.h" -#include "Arduino.h" -#include "SPI.h" -#include "WiFiNINA.h" -#include "Wippersnapper.h" - -#define NINAFWVER \ - "1.6.0" /*!< min. nina-fw version compatible with this library. */ - -#define SPIWIFI SPI /*!< Instance of SPI interface used by an AirLift. */ - -extern Wippersnapper WS; -/****************************************************************************/ -/*! - @brief Class for using the AirLift Co-Processor network iface. -*/ -/****************************************************************************/ -class Wippersnapper_AIRLIFT : public Wippersnapper { - -public: - /**************************************************************************/ - /*! - @brief Initializes the Adafruit IO class for AirLift devices. - */ - /**************************************************************************/ - Wippersnapper_AIRLIFT() : Wippersnapper() { - _ssPin = 10; - _ackPin = 7; - _rstPin = 5; - _gpio0Pin = -1; - _wifi = &SPIWIFI; - _ssid = 0; - _pass = 0; - _mqtt_client = new WiFiSSLClient; - - // setup ESP32 co-processor pins during init. - WiFi.setPins(_ssPin, _ackPin, _rstPin, _gpio0Pin, _wifi); - } - - /**************************************************************************/ - /*! - @brief Destructor for the Adafruit IO AirLift class. - */ - /**************************************************************************/ - ~Wippersnapper_AIRLIFT() { - if (_mqtt) - delete _mqtt; - } - - /**********************************************************/ - /*! - @brief Sets the WiFi client's ssid and password. - @param ssid - Wireless network's SSID. - @param ssidPassword - Wireless network's password. - */ - /**********************************************************/ - void set_ssid_pass(const char *ssid, const char *ssidPassword) { - _ssid = ssid; - _pass = ssidPassword; - } - - /**********************************************************/ - /*! - @brief Sets the WiFi client's ssid and password from the - secrets.json provisioning file. - */ - /**********************************************************/ - void set_ssid_pass() { - _ssid = WS._config.network.ssid; - _pass = WS._config.network.pass; - } - - /***********************************************************/ - /*! - @brief Performs a scan of local WiFi networks. - @returns True if `_network_ssid` is found, False otherwise. - */ - /***********************************************************/ - bool check_valid_ssid() { - // Disconnect WiFi from an AP if it was previously connected - WiFi.disconnect(); - delay(100); - - // Perform a network scan - int n = WiFi.scanNetworks(); - if (n == 0) { - WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!"); - return false; - } - - // Was the network within secrets.json found? - for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i)) == 0) - return true; - } - - // User-set network not found, print scan results to serial console - WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); - WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); - for (int i = 0; i < n; ++i) { - WS_DEBUG_PRINT(WiFi.SSID(i)); - WS_DEBUG_PRINT(" "); - WS_DEBUG_PRINT(WiFi.RSSI(i)); - WS_DEBUG_PRINTLN("dB"); - } - - return false; - } - - /********************************************************/ - /*! - @brief Sets the WiFi client. - @param wifi - Instance of SPIClass. - */ - /********************************************************/ - void set_wifi(SPIClass *wifi) { - _wifi = wifi; - _mqtt_client = new WiFiSSLClient; - } - - /********************************************************/ - /*! - @brief Configures ESP32 "AirLift" pins. - @param ssPin - ESP32 S.S. pin. - @param ackPin - ESP32 ACK pin. - @param rstPin - ESP32 RST pin. - @param gpio0Pin - ESP32 GPIO0 pin. - */ - /********************************************************/ - void set_airlift_pins(int ssPin, int ackPin, int rstPin, int gpio0Pin) { - _ssPin = ssPin; - _ackPin = ackPin; - _rstPin = rstPin; - _gpio0Pin = gpio0Pin; - } - - /********************************************************/ - /*! - @brief Checks the version of an ESP32 module running - nina-fw. - @returns True if matches min. required to run - WipperSnapper, False otherwise. - */ - /********************************************************/ - bool firmwareCheck() { - _fv = WiFi.firmwareVersion(); - if (_fv < NINAFWVER) - return false; - return true; - } - - /********************************************************/ - /*! - @brief Gets the ESP32's unique client identifier. - @note For the ESP32, the UID is the MAC address. - */ - /********************************************************/ - void getMacAddr() { - uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - WiFi.macAddress(mac); - memcpy(WS._macAddr, mac, sizeof(mac)); - } - - /********************************************************/ - /*! - @brief Initializes the MQTT client. - @param clientID - MQTT client identifier - */ - /********************************************************/ - void setupMQTTClient(const char *clientID) { - 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); - } - - /********************************************************/ - /*! - @brief Returns the network status of an ESP32 module. - @return ws_status_t - */ - /********************************************************/ - ws_status_t networkStatus() { - switch (WiFi.status()) { - case WL_CONNECTED: - return WS_NET_CONNECTED; - case WL_CONNECT_FAILED: - return WS_NET_CONNECT_FAILED; - case WL_IDLE_STATUS: - return WS_IDLE; - default: - return WS_NET_DISCONNECTED; - } - } - - /*******************************************************************/ - /*! - @brief Returns the type of network connection used by Wippersnapper - @return AIRLIFT - */ - /*******************************************************************/ - const char *connectionType() { return "AIRLIFT"; } - -protected: - const char *_ssid; /*!< Network SSID. */ - const char *_pass; /*!< Network password. */ - String _fv; /*!< nina-fw firmware version. */ - int _ssPin = -1; /*!< SPI S.S. pin. */ - int _ackPin = -1; /*!< SPI ACK pin. */ - int _rstPin = -1; /*!< SPI RST pin. */ - int _gpio0Pin = -1; /*!< SPI GPIO0 pin, unused. */ - WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */ - SPIClass *_wifi; /*!< Instance of the SPI bus used by the AirLift. */ - - /**************************************************************************/ - /*! - @brief Establishes a connection with the wireless network. - */ - /**************************************************************************/ - void _connect() { - if (strlen(_ssid) == 0) { - _status = WS_SSID_INVALID; - } else { - - // validate co-processor is physically connected connection - if (WiFi.status() == WL_NO_MODULE) { - WS_DEBUG_PRINT("No ESP32 module detected!"); - return; - } - - // validate co-processor's firmware version - if (!firmwareCheck()) - WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the " - "latest version."); - - // disconnect from possible previous connection - _disconnect(); - - WiFi.begin(_ssid, _pass); - _status = WS_NET_DISCONNECTED; - } - } - - /**************************************************************************/ - /*! - @brief Disconnects from the wireless network. - */ - /**************************************************************************/ - void _disconnect() { - WiFi.disconnect(); - delay(500); - } -}; - +/*! + * @file Wippersnapper_AIRLIFT.h + * + * This is a driver for using the Adafruit AirLift + * ESP32 Co-Processor's network interface with Wippersnapper. + * + * The ESP32 AirLift uses SPI to communicate. Three lines (CS, ACK, RST) are + * required to communicate with the ESP32 AirLift. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries. + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef WIPPERSNAPPER_AIRLIFT_H +#define WIPPERSNAPPER_AIRLIFT_H + +#include "Adafruit_MQTT.h" +#include "Adafruit_MQTT_Client.h" +#include "Arduino.h" +#include "SPI.h" +#include "WiFiNINA.h" +#include "Wippersnapper.h" + +#define NINAFWVER \ + "1.6.0" /*!< min. nina-fw version compatible with this library. */ + +#define SPIWIFI SPI /*!< Instance of SPI interface used by an AirLift. */ + +extern Wippersnapper WS; +/****************************************************************************/ +/*! + @brief Class for using the AirLift Co-Processor network iface. +*/ +/****************************************************************************/ +class Wippersnapper_AIRLIFT : public Wippersnapper { + +public: + /**************************************************************************/ + /*! + @brief Initializes the Adafruit IO class for AirLift devices. + */ + /**************************************************************************/ + Wippersnapper_AIRLIFT() : Wippersnapper() { + _ssPin = 10; + _ackPin = 7; + _rstPin = 5; + _gpio0Pin = -1; + _wifi = &SPIWIFI; + _ssid = 0; + _pass = 0; + _mqtt_client = new WiFiSSLClient; + + // setup ESP32 co-processor pins during init. + WiFi.setPins(_ssPin, _ackPin, _rstPin, _gpio0Pin, _wifi); + } + + /**************************************************************************/ + /*! + @brief Destructor for the Adafruit IO AirLift class. + */ + /**************************************************************************/ + ~Wippersnapper_AIRLIFT() { + if (_mqtt) + delete _mqtt; + } + + /**********************************************************/ + /*! + @brief Sets the WiFi client's ssid and password. + @param ssid + Wireless network's SSID. + @param ssidPassword + Wireless network's password. + */ + /**********************************************************/ + void set_ssid_pass(const char *ssid, const char *ssidPassword) { + _ssid = ssid; + _pass = ssidPassword; + } + + /**********************************************************/ + /*! + @brief Sets the WiFi client's ssid and password from the + secrets.json provisioning file. + */ + /**********************************************************/ + void set_ssid_pass() { + _ssid = WS._config.network.ssid; + _pass = WS._config.network.pass; + } + + /***********************************************************/ + /*! + @brief Performs a scan of local WiFi networks. + @returns True if `_network_ssid` is found, False otherwise. + */ + /***********************************************************/ + bool check_valid_ssid() { + // Disconnect WiFi from an AP if it was previously connected + WiFi.disconnect(); + delay(100); + + // Perform a network scan + int n = WiFi.scanNetworks(); + if (n == 0) { + WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!"); + return false; + } + + // Was the network within secrets.json found? + for (int i = 0; i < n; ++i) { + if (strcmp(_ssid, WiFi.SSID(i)) == 0) + return true; + } + + // User-set network not found, print scan results to serial console + WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); + WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); + for (int i = 0; i < n; ++i) { + WS_DEBUG_PRINT(WiFi.SSID(i)); + WS_DEBUG_PRINT(" "); + WS_DEBUG_PRINT(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN("dB"); + } + + return false; + } + + /********************************************************/ + /*! + @brief Sets the WiFi client. + @param wifi + Instance of SPIClass. + */ + /********************************************************/ + void set_wifi(SPIClass *wifi) { + _wifi = wifi; + _mqtt_client = new WiFiSSLClient; + } + + /********************************************************/ + /*! + @brief Configures ESP32 "AirLift" pins. + @param ssPin + ESP32 S.S. pin. + @param ackPin + ESP32 ACK pin. + @param rstPin + ESP32 RST pin. + @param gpio0Pin + ESP32 GPIO0 pin. + */ + /********************************************************/ + void set_airlift_pins(int ssPin, int ackPin, int rstPin, int gpio0Pin) { + _ssPin = ssPin; + _ackPin = ackPin; + _rstPin = rstPin; + _gpio0Pin = gpio0Pin; + } + + /********************************************************/ + /*! + @brief Checks the version of an ESP32 module running + nina-fw. + @returns True if matches min. required to run + WipperSnapper, False otherwise. + */ + /********************************************************/ + bool firmwareCheck() { + _fv = WiFi.firmwareVersion(); + if (_fv < NINAFWVER) + return false; + return true; + } + + /********************************************************/ + /*! + @brief Gets the ESP32's unique client identifier. + @note For the ESP32, the UID is the MAC address. + */ + /********************************************************/ + void getMacAddr() { + uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + WiFi.macAddress(mac); + memcpy(WS._macAddr, mac, sizeof(mac)); + } + + /********************************************************/ + /*! + @brief Initializes the MQTT client. + @param clientID + MQTT client identifier + */ + /********************************************************/ + void setupMQTTClient(const char *clientID) { + 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); + } + + /********************************************************/ + /*! + @brief Returns the network status of an ESP32 module. + @return ws_status_t + */ + /********************************************************/ + ws_status_t networkStatus() { + switch (WiFi.status()) { + case WL_CONNECTED: + return WS_NET_CONNECTED; + case WL_CONNECT_FAILED: + return WS_NET_CONNECT_FAILED; + case WL_IDLE_STATUS: + return WS_IDLE; + default: + return WS_NET_DISCONNECTED; + } + } + + /*******************************************************************/ + /*! + @brief Returns the type of network connection used by Wippersnapper + @return AIRLIFT + */ + /*******************************************************************/ + const char *connectionType() { return "AIRLIFT"; } + +protected: + const char *_ssid; /*!< Network SSID. */ + const char *_pass; /*!< Network password. */ + String _fv; /*!< nina-fw firmware version. */ + int _ssPin = -1; /*!< SPI S.S. pin. */ + int _ackPin = -1; /*!< SPI ACK pin. */ + int _rstPin = -1; /*!< SPI RST pin. */ + int _gpio0Pin = -1; /*!< SPI GPIO0 pin, unused. */ + WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */ + SPIClass *_wifi; /*!< Instance of the SPI bus used by the AirLift. */ + + /**************************************************************************/ + /*! + @brief Establishes a connection with the wireless network. + */ + /**************************************************************************/ + void _connect() { + if (strlen(_ssid) == 0) { + _status = WS_SSID_INVALID; + } else { + + // validate co-processor is physically connected connection + if (WiFi.status() == WL_NO_MODULE) { + WS_DEBUG_PRINT("No ESP32 module detected!"); + return; + } + + // validate co-processor's firmware version + if (!firmwareCheck()) + WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the " + "latest version."); + + // disconnect from possible previous connection + _disconnect(); + + // reset the esp32 if possible + if (_rstPin != -1) { + WS_DEBUG("Resetting ESP32..."); + pinMode(_rstPin, OUTPUT); + digitalWrite(_rstPin, LOW); + delay(10); + digitalWrite(_rstPin, HIGH); + delay(10); + } + // wait for the ESP32 to boot + delay(1000); + + WS_DEBUG_PRINT("Connecting to "); + WS_DEBUG_PRINTLN(_ssid); + WiFi.begin(_ssid, _pass); + _status = WS_NET_DISCONNECTED; + } + } + + /**************************************************************************/ + /*! + @brief Disconnects from the wireless network. + */ + /**************************************************************************/ + void _disconnect() { + WiFi.disconnect(); + delay(500); + } +}; + #endif // WIPPERSNAPPER_AIRLIFT_H \ No newline at end of file diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h index aa56c3b40..9b618143f 100644 --- a/src/network_interfaces/Wippersnapper_WIFININA.h +++ b/src/network_interfaces/Wippersnapper_WIFININA.h @@ -1,275 +1,291 @@ -/*! - * @file Wippersnapper_WIFININA.h - * - * Network interface for the ublox wifi module on the - * Arduino MKR WiFi 1010, Arduino Nano 33 IoT and Arduino UNO WiFi Rev.2. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2021 for Adafruit Industries. - * - * MIT license, all text here must be included in any redistribution. - * - */ - -#ifndef WIPPERSNAPPER_WIFININA_H -#define WIPPERSNAPPER_WIFININA_H -#include -#include -#include -#include -#include - -#include "Wippersnapper.h" - -#define SPIWIFI \ - SPI /*!< Instance of SPI interface used by an external uBlox module. */ - -extern Wippersnapper WS; -/****************************************************************************/ -/*! - @brief Class for using the AirLift Co-Processor network iface. -*/ -/****************************************************************************/ -class Wippersnapper_WIFININA : public Wippersnapper { - -public: - /**************************************************************************/ - /*! - @brief Initializes the Adafruit IO class for ublox devices. - @param aioUsername - Adafruit IO username - @param aioKey - Adafruit IO key - @param netSSID - Wireless Network SSID - @param netPass - Wireless Network password - */ - /**************************************************************************/ - Wippersnapper_WIFININA(const char *aioUsername, const char *aioKey, - const char *netSSID, const char *netPass) - : Wippersnapper() { - _ssid = netSSID; - _pass = netPass; - _username = aioUsername; - _key = aioKey; - - _wifi = &SPIWIFI; - _mqtt_client = new WiFiSSLClient; - } - - /**************************************************************************/ - /*! - @brief Destructor for the Adafruit IO ublox class. - */ - /**************************************************************************/ - ~Wippersnapper_WIFININA() { - if (_mqtt) - delete _mqtt; - } - - /****************************************************************************/ - /*! - @brief Configures the device's Adafruit IO credentials. This method - should be used only if filesystem-backed provisioning is - not avaliable. - */ - /****************************************************************************/ - void set_user_key() { - strlcpy(WS._config.aio_user, _username, sizeof(WS._config.aio_user)); - strlcpy(WS._config.aio_key, _key, sizeof(WS._config.aio_key)); - } - - /**********************************************************/ - /*! - @brief Sets the WiFi client's ssid and password. - @param ssid - Wireless network's SSID. - @param ssidPassword - Wireless network's password. - */ - /**********************************************************/ - void set_ssid_pass(const char *ssid, const char *ssidPassword) { - strlcpy(WS._config.network.ssid, ssid, sizeof(WS._config.network.ssid)); - strlcpy(WS._config.network.pass, ssidPassword, - sizeof(WS._config.network.pass)); - } - - /**********************************************************/ - /*! - @brief Sets the WiFi client's ssid and password from the - header file's credentials. - */ - /**********************************************************/ - void set_ssid_pass() { - strlcpy(WS._config.network.ssid, _ssid, sizeof(WS._config.network.ssid)); - strlcpy(WS._config.network.pass, _pass, sizeof(WS._config.network.pass)); - } - - /***********************************************************/ - /*! - @brief Performs a scan of local WiFi networks. - @returns True if `_network_ssid` is found, False otherwise. - */ - /***********************************************************/ - bool check_valid_ssid() { - // Set WiFi to station mode and disconnect from an AP if it was previously - // connected - WiFi.disconnect(); - delay(100); - - // Perform a network scan - int n = WiFi.scanNetworks(); - if (n == 0) { - WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!"); - return false; - } - - // Was the network within secrets.json found? - for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i)) == 0) - return true; - } - - // User-set network not found, print scan results to serial console - WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); - WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); - for (int i = 0; i < n; ++i) { - WS_DEBUG_PRINT(WiFi.SSID(i)); - WS_DEBUG_PRINT(" "); - WS_DEBUG_PRINT(WiFi.RSSI(i)); - WS_DEBUG_PRINTLN("dB"); - } - - return false; - } - - /********************************************************/ - /*! - @brief Sets the WiFi client. - @param wifi - Instance of SPIClass. - */ - /********************************************************/ - void set_wifi(SPIClass *wifi) { - _wifi = wifi; - _mqtt_client = new WiFiSSLClient; - } - - /***********************************************************/ - /*! - @brief Checks the nina-fw version on the module. - @return True if firmware on the ublox module matches - the latest version of the library, False otherwise. - */ - /***********************************************************/ - bool firmwareCheck() { - String fv = WiFi.firmwareVersion(); - if (fv < WIFI_FIRMWARE_LATEST_VERSION) - return false; - return true; - } - - /********************************************************/ - /*! - @brief Gets the ESP32's unique client identifier. - @note For the ESP32, the UID is the MAC address. - */ - /********************************************************/ - void getMacAddr() { - uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - WiFi.macAddress(mac); - memcpy(WS._macAddr, mac, sizeof(mac)); - } - - /********************************************************/ - /*! - @brief Initializes the MQTT client. - @param clientID - MQTT client identifier - */ - /********************************************************/ - void setupMQTTClient(const char *clientID) { - 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); - } - - /********************************************************/ - /*! - @brief Returns the network status of an ESP32 module. - @return ws_status_t - */ - /********************************************************/ - ws_status_t networkStatus() { - switch (WiFi.status()) { - case WL_CONNECTED: - return WS_NET_CONNECTED; - case WL_CONNECT_FAILED: - return WS_NET_CONNECT_FAILED; - case WL_IDLE_STATUS: - return WS_IDLE; - default: - return WS_NET_DISCONNECTED; - } - } - - /*******************************************************************/ - /*! - @brief Returns the type of network connection used by Wippersnapper - @return AIRLIFT - */ - /*******************************************************************/ - const char *connectionType() { return "AIRLIFT"; } - -protected: - const char *_ssid; /*!< Network SSID. */ - const char *_pass; /*!< Network password. */ - const char *_username; /*!< Adafruit IO username. */ - const char *_key; /*!< Adafruit IO key. */ - - WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */ - SPIClass *_wifi; /*!< Instance of the SPI bus used by the ublox. */ - - /**************************************************************************/ - /*! - @brief Establishes a connection with the wireless network. - */ - /**************************************************************************/ - void _connect() { - - // check if co-processor connected first - if (WiFi.status() == WL_NO_MODULE) - errorWriteHang("No WiFi Module Detected!"); - - // validate the nina-fw version - if (!firmwareCheck()) - errorWriteHang("Please upgrade the firmware on the ESP module to the " - "latest version."); - - if (strlen(_ssid) == 0) { - _status = WS_SSID_INVALID; - } else { - // disconnect from possible previous connection - _disconnect(); - - WiFi.begin(_ssid, _pass); - _status = WS_NET_DISCONNECTED; - } - } - - /**************************************************************************/ - /*! - @brief Disconnects from the wireless network. - */ - /**************************************************************************/ - void _disconnect() { - WiFi.disconnect(); - delay(500); - } -}; - +/*! + * @file Wippersnapper_WIFININA.h + * + * Network interface for the ublox wifi module on the + * Arduino MKR WiFi 1010, Arduino Nano 33 IoT and Arduino UNO WiFi Rev.2. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2021 for Adafruit Industries. + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef WIPPERSNAPPER_WIFININA_H +#define WIPPERSNAPPER_WIFININA_H +#include +#include +#include +#include +#include + +#include "Wippersnapper.h" + +#define SPIWIFI \ + SPI /*!< Instance of SPI interface used by an external uBlox module. */ + +extern Wippersnapper WS; +/****************************************************************************/ +/*! + @brief Class for using the AirLift Co-Processor network iface. +*/ +/****************************************************************************/ +class Wippersnapper_WIFININA : public Wippersnapper { + +public: + /**************************************************************************/ + /*! + @brief Initializes the Adafruit IO class for ublox devices. + @param aioUsername + Adafruit IO username + @param aioKey + Adafruit IO key + @param netSSID + Wireless Network SSID + @param netPass + Wireless Network password + */ + /**************************************************************************/ + Wippersnapper_WIFININA(const char *aioUsername, const char *aioKey, + const char *netSSID, const char *netPass) + : Wippersnapper() { + _ssid = netSSID; + _pass = netPass; + _username = aioUsername; + _key = aioKey; + + _wifi = &SPIWIFI; + _mqtt_client = new WiFiSSLClient; + } + + /**************************************************************************/ + /*! + @brief Destructor for the Adafruit IO ublox class. + */ + /**************************************************************************/ + ~Wippersnapper_WIFININA() { + if (_mqtt) + delete _mqtt; + } + + /****************************************************************************/ + /*! + @brief Configures the device's Adafruit IO credentials. This method + should be used only if filesystem-backed provisioning is + not avaliable. + */ + /****************************************************************************/ + void set_user_key() { + strlcpy(WS._config.aio_user, _username, sizeof(WS._config.aio_user)); + strlcpy(WS._config.aio_key, _key, sizeof(WS._config.aio_key)); + } + + /**********************************************************/ + /*! + @brief Sets the WiFi client's ssid and password. + @param ssid + Wireless network's SSID. + @param ssidPassword + Wireless network's password. + */ + /**********************************************************/ + void set_ssid_pass(const char *ssid, const char *ssidPassword) { + strlcpy(WS._config.network.ssid, ssid, sizeof(WS._config.network.ssid)); + strlcpy(WS._config.network.pass, ssidPassword, + sizeof(WS._config.network.pass)); + } + + /**********************************************************/ + /*! + @brief Sets the WiFi client's ssid and password from the + header file's credentials. + */ + /**********************************************************/ + void set_ssid_pass() { + strlcpy(WS._config.network.ssid, _ssid, sizeof(WS._config.network.ssid)); + strlcpy(WS._config.network.pass, _pass, sizeof(WS._config.network.pass)); + } + + /***********************************************************/ + /*! + @brief Performs a scan of local WiFi networks. + @returns True if `_network_ssid` is found, False otherwise. + */ + /***********************************************************/ + bool check_valid_ssid() { + // Set WiFi to station mode and disconnect from an AP if it was previously + // connected + WiFi.disconnect(); + delay(100); + + // Perform a network scan + int n = WiFi.scanNetworks(); + if (n == 0) { + WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!"); + return false; + } + + // Was the network within secrets.json found? + for (int i = 0; i < n; ++i) { + if (strcmp(_ssid, WiFi.SSID(i)) == 0) + return true; + } + + // User-set network not found, print scan results to serial console + WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); + WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); + for (int i = 0; i < n; ++i) { + WS_DEBUG_PRINT(WiFi.SSID(i)); + WS_DEBUG_PRINT(" "); + WS_DEBUG_PRINT(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN("dB"); + } + + return false; + } + + /********************************************************/ + /*! + @brief Sets the WiFi client. + @param wifi + Instance of SPIClass. + */ + /********************************************************/ + void set_wifi(SPIClass *wifi) { + _wifi = wifi; + _mqtt_client = new WiFiSSLClient; + } + + /***********************************************************/ + /*! + @brief Checks the nina-fw version on the module. + @return True if firmware on the ublox module matches + the latest version of the library, False otherwise. + */ + /***********************************************************/ + bool firmwareCheck() { + String fv = WiFi.firmwareVersion(); + if (fv < WIFI_FIRMWARE_LATEST_VERSION) + return false; + return true; + } + + /********************************************************/ + /*! + @brief Gets the ESP32's unique client identifier. + @note For the ESP32, the UID is the MAC address. + */ + /********************************************************/ + void getMacAddr() { + uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + WiFi.macAddress(mac); + memcpy(WS._macAddr, mac, sizeof(mac)); + } + + /********************************************************/ + /*! + @brief Initializes the MQTT client. + @param clientID + MQTT client identifier + */ + /********************************************************/ + void setupMQTTClient(const char *clientID) { + 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); + } + + /********************************************************/ + /*! + @brief Returns the network status of an ESP32 module. + @return ws_status_t + */ + /********************************************************/ + ws_status_t networkStatus() { + switch (WiFi.status()) { + case WL_CONNECTED: + return WS_NET_CONNECTED; + case WL_CONNECT_FAILED: + return WS_NET_CONNECT_FAILED; + case WL_IDLE_STATUS: + return WS_IDLE; + default: + return WS_NET_DISCONNECTED; + } + } + + /*******************************************************************/ + /*! + @brief Returns the type of network connection used by Wippersnapper + @return AIRLIFT + */ + /*******************************************************************/ + const char *connectionType() { return "AIRLIFT"; } + +protected: + const char *_ssid; /*!< Network SSID. */ + const char *_pass; /*!< Network password. */ + const char *_username; /*!< Adafruit IO username. */ + const char *_key; /*!< Adafruit IO key. */ + + WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */ + SPIClass *_wifi; /*!< Instance of the SPI bus used by the ublox. */ + + /**************************************************************************/ + /*! + @brief Establishes a connection with the wireless network. + */ + /**************************************************************************/ + void _connect() { + + // check if co-processor connected first + if (WiFi.status() == WL_NO_MODULE) + errorWriteHang("No WiFi Module Detected!"); + + // validate the nina-fw version + if (!firmwareCheck()) + errorWriteHang("Please upgrade the firmware on the ESP module to the " + "latest version."); + + if (strlen(_ssid) == 0) { + _status = WS_SSID_INVALID; + } else { + // disconnect from possible previous connection + _disconnect(); + +#if defined(ESP32_RESETN) + // reset the esp32 if possible, better if we didn't do this first time + if (ESP32_RESETN != -1) { + WS_DEBUG("Resetting ESP32..."); + pinMode(ESP32_RESETN, OUTPUT); + digitalWrite(ESP32_RESETN, LOW); + delay(10); + digitalWrite(ESP32_RESETN, HIGH); + delay(10); + } + // wait for the ESP32 to boot + delay(1000); +#endif + + WS_DEBUG_PRINT("Connecting to "); + WS_DEBUG_PRINTLN(_ssid); + WiFi.begin(_ssid, _pass); + _status = WS_NET_DISCONNECTED; + } + } + + /**************************************************************************/ + /*! + @brief Disconnects from the wireless network. + */ + /**************************************************************************/ + void _disconnect() { + WiFi.disconnect(); + delay(500); + } +}; + #endif // WIPPERSNAPPER_WIFININA_H \ No newline at end of file From 5585061b011339c3591202b3826aed90ebb72659 Mon Sep 17 00:00:00 2001 From: tyeth Date: Wed, 29 May 2024 16:23:04 +0100 Subject: [PATCH 15/54] Correct WIFININA reset pin name inline with Arduino Nano_33_iot/MkrWifi1010 --- .../Wippersnapper_WIFININA.h | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h index 9b618143f..87de9087f 100644 --- a/src/network_interfaces/Wippersnapper_WIFININA.h +++ b/src/network_interfaces/Wippersnapper_WIFININA.h @@ -256,14 +256,23 @@ class Wippersnapper_WIFININA : public Wippersnapper { // disconnect from possible previous connection _disconnect(); -#if defined(ESP32_RESETN) +// no clear recommendation, all three defined for both boards, future-proofing +#if defined(NINA_RESETN) +#define NINA_RESET_PIN NINA_RESETN +#elif defined(SPIWIFI_RESET) +#define NINA_RESET_PIN SPIWIFI_RESET +#elif defined(ESP32_RESETN) +#define NINA_RESET_PIN ESP32_RESETN +#endif + +#if defined(NINA_RESET_PIN) // reset the esp32 if possible, better if we didn't do this first time - if (ESP32_RESETN != -1) { + if (NINA_RESET_PIN != -1) { WS_DEBUG("Resetting ESP32..."); - pinMode(ESP32_RESETN, OUTPUT); - digitalWrite(ESP32_RESETN, LOW); + pinMode(NINA_RESET_PIN, OUTPUT); + digitalWrite(NINA_RESET_PIN, LOW); delay(10); - digitalWrite(ESP32_RESETN, HIGH); + digitalWrite(NINA_RESET_PIN, HIGH); delay(10); } // wait for the ESP32 to boot From 9adaeb196006c410a9a8944f71744c394b97ef16 Mon Sep 17 00:00:00 2001 From: tyeth Date: Wed, 29 May 2024 17:58:31 +0100 Subject: [PATCH 16/54] Add RSSI to network interfaces and log on join or ping --- src/Wippersnapper.cpp | 17 +- src/Wippersnapper.h | 892 +++++++++--------- .../Wippersnapper_AIRLIFT.h | 607 ++++++------ src/network_interfaces/Wippersnapper_ESP32.h | 11 + .../Wippersnapper_ESP8266.h | 11 + .../Wippersnapper_WIFININA.h | 11 + src/network_interfaces/ws_networking_pico.h | 13 +- 7 files changed, 817 insertions(+), 745 deletions(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index a94a0943d..e191f2f3d 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -182,6 +182,15 @@ void Wippersnapper::getMacAddr() { WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); } +/****************************************************************************/ +/*! + @brief Gets the network's RSSI. +*/ +/****************************************************************************/ +void Wippersnapper::getRSSI() { + WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); +} + /****************************************************************************/ /*! @brief Sets up the MQTT client session. @@ -2391,7 +2400,8 @@ void Wippersnapper::runNetFSM() { haltError("ERROR: Unable to find WiFi network, rebooting soon...", WS_LED_STATUS_WIFI_CONNECTING); } - WS_DEBUG_PRINTLN("SSID found!"); + WS_DEBUG_PRINT("SSID found! RSSI: "); + WS_DEBUG_PRINTLN(WS._RSSI); // Attempt to connect to wireless network maxAttempts = 5; while (maxAttempts > 0) { @@ -2556,6 +2566,9 @@ void Wippersnapper::pingBroker() { WS_DEBUG_PRINTLN("FAILURE!"); } _prv_ping = millis(); + WS.getRSSI(); + WS_DEBUG_PRINT("WiFi RSSI: "); + WS_DEBUG_PRINTLN(WS._RSSI); } // blink status LED every STATUS_LED_KAT_BLINK_TIME millis if (millis() > (_prvKATBlink + STATUS_LED_KAT_BLINK_TIME)) { @@ -2865,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 87fffc62c..76326ef8d 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -1,445 +1,447 @@ -/*! - * @file Wippersnapper.h - * - * This is the documentation for Adafruit's Wippersnapper firmware for the - * Arduino platform. It is designed specifically to work with - * Adafruit IO Wippersnapper IoT platform. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2020-2024 for Adafruit Industries. - * - * BSD license, all text here must be included in any redistribution. - * - */ - -#ifndef WIPPERSNAPPER_H -#define WIPPERSNAPPER_H - -// Cpp STD -#include - -// Nanopb dependencies -#include -#include -#include -#include - -#include // description.proto -#include // signal.proto - -// External libraries -#include "Adafruit_MQTT.h" // MQTT Client -#include "Adafruit_SleepyDog.h" // Watchdog -#include "Arduino.h" // Wiring -#include // SPI - -// Wippersnapper API Helpers -#include "Wippersnapper_Boards.h" -#include "components/statusLED/Wippersnapper_StatusLED.h" -#include "provisioning/ConfigJson.h" - -#define WS_DEBUG ///< Define to enable debugging to serial terminal -#define WS_PRINTER Serial ///< Where debug messages will be printed - -// Define actual debug output functions when necessary. -#ifdef WS_DEBUG -#define WS_DEBUG_PRINT(...) \ - { WS_PRINTER.print(__VA_ARGS__); } ///< Prints debug output. -#define WS_DEBUG_PRINTLN(...) \ - { WS_PRINTER.println(__VA_ARGS__); } ///< Prints line from debug output. -#define WS_DEBUG_PRINTHEX(...) \ - { WS_PRINTER.print(__VA_ARGS__, HEX); } ///< Prints debug output. -#else -#define WS_DEBUG_PRINT(...) \ - {} ///< Prints debug output -#define WS_DEBUG_PRINTLN(...) \ - {} ///< Prints line from debug output. -#endif - -// Wippersnapper pb helpers -#include - -// Wippersnapper components -#include "components/analogIO/Wippersnapper_AnalogIO.h" -#include "components/digitalIO/Wippersnapper_DigitalGPIO.h" -#include "components/i2c/WipperSnapper_I2C.h" - -// LEDC-Manager, ESP32-only -#ifdef ARDUINO_ARCH_ESP32 -#include "components/ledc/ws_ledc.h" -#endif - -// Display -#ifdef USE_DISPLAY -#include "display/ws_display_driver.h" -#include "display/ws_display_ui_helper.h" -#endif - -#include "components/ds18x20/ws_ds18x20.h" -#include "components/pixels/ws_pixels.h" -#include "components/pwm/ws_pwm.h" -#include "components/servo/ws_servo.h" -#include "components/uart/ws_uart.h" - -#if defined(USE_TINYUSB) -#include "provisioning/tinyusb/Wippersnapper_FS.h" -#endif - -#if defined(USE_LITTLEFS) -#include "provisioning/littlefs/WipperSnapper_LittleFS.h" -#endif - -#define WS_VERSION \ - "1.0.0-beta.85" ///< WipperSnapper app. version (semver-formatted) - -// Reserved Adafruit IO MQTT topics -#define TOPIC_IO_THROTTLE "/throttle" ///< Adafruit IO Throttle MQTT Topic -#define TOPIC_IO_ERRORS "/errors" ///< Adafruit IO Error MQTT Topic - -// Reserved Wippersnapper topics -#define TOPIC_WS "/wprsnpr/" ///< WipperSnapper topic -#define TOPIC_INFO "/info/" ///< Registration sub-topic -#define TOPIC_SIGNALS "/signals/" ///< Signals sub-topic -#define TOPIC_I2C "/i2c" ///< I2C sub-topic -#define MQTT_TOPIC_PIXELS_DEVICE \ - "/signals/device/pixel" ///< Pixels device->broker topic -#define MQTT_TOPIC_PIXELS_BROKER \ - "/signals/broker/pixel" ///< Pixels broker->device topic - -/** Defines the Adafruit IO connection status */ -typedef enum { - WS_IDLE = 0, // Waiting for connection establishement - WS_NET_DISCONNECTED = 1, // Network disconnected - WS_DISCONNECTED = 2, // Disconnected from Adafruit IO - WS_FINGERPRINT_UNKOWN = 3, // Unknown WS_SSL_FINGERPRINT - - WS_NET_CONNECT_FAILED = 10, // Failed to connect to network - WS_CONNECT_FAILED = 11, // Failed to connect to Adafruit IO - WS_FINGERPRINT_INVALID = 12, // Unknown WS_SSL_FINGERPRINT - WS_AUTH_FAILED = 13, // Invalid Adafruit IO login credentials provided. - WS_SSID_INVALID = - 14, // SSID is "" or otherwise invalid, connection not attempted - - WS_NET_CONNECTED = 20, // Connected to Adafruit IO - WS_CONNECTED = 21, // Connected to network - WS_CONNECTED_INSECURE = 22, // Insecurely (non-SSL) connected to network - WS_FINGERPRINT_UNSUPPORTED = 23, // Unsupported WS_SSL_FINGERPRINT - WS_FINGERPRINT_VALID = 24, // Valid WS_SSL_FINGERPRINT - WS_BOARD_DESC_INVALID = 25, // Unable to send board description - WS_BOARD_RESYNC_FAILED = 26 // Board sync failure -} ws_status_t; - -/** Defines the Adafruit IO MQTT broker's connection return codes */ -typedef enum { - WS_MQTT_CONNECTED = 0, // Connected - WS_MQTT_INVALID_PROTOCOL = 1, // Invalid mqtt protocol - WS_MQTT_INVALID_CID = 2, // Client id rejected - WS_MQTT_SERVICE_UNAVALIABLE = 3, // Malformed user/pass - WS_MQTT_INVALID_USER_PASS = 4, // Unauthorized access to resource - WS_MQTT_UNAUTHORIZED = 5, // MQTT service unavailable - WS_MQTT_THROTTLED = 6, // Account throttled - WS_MQTT_BANNED = 7 // Account banned -} ws_mqtt_status_t; - -/** Defines the Wippersnapper client's hardware registration status */ -typedef enum { - WS_BOARD_DEF_IDLE, - WS_BOARD_DEF_SEND_FAILED, - WS_BOARD_DEF_SENT, - WS_BOARD_DEF_OK, - WS_BOARD_DEF_INVALID, - WS_BOARD_DEF_UNSPECIFIED -} ws_board_status_t; - -/** Defines the Wippersnapper client's network status */ -typedef enum { - FSM_NET_IDLE, - FSM_NET_CONNECTED, - FSM_MQTT_CONNECTED, - FSM_NET_CHECK_MQTT, - FSM_NET_CHECK_NETWORK, - FSM_NET_ESTABLISH_NETWORK, - FSM_NET_ESTABLISH_MQTT, -} fsm_net_t; - -#define WS_WDT_TIMEOUT 60000 ///< WDT timeout -/* MQTT Configuration */ -#define WS_KEEPALIVE_INTERVAL_MS \ - 5000 ///< Session keepalive interval time, in milliseconds - -#define WS_MQTT_MAX_PAYLOAD_SIZE \ - 512 ///< MAXIMUM expected payload size, in bytes - -class Wippersnapper_DigitalGPIO; -class Wippersnapper_AnalogIO; -class Wippersnapper_FS; -class WipperSnapper_LittleFS; -#ifdef USE_DISPLAY -class ws_display_driver; -class ws_display_ui_helper; -#endif -#ifdef ARDUINO_ARCH_ESP32 -class ws_ledc; -#endif -class WipperSnapper_Component_I2C; -class ws_servo; -class ws_pwm; -class ws_ds18x20; -class ws_pixels; -class ws_uart; - -/**************************************************************************/ -/*! - @brief Class that provides storage and functions for the Adafruit IO - Wippersnapper interface. -*/ -/**************************************************************************/ -class Wippersnapper { -public: - Wippersnapper(); - virtual ~Wippersnapper(); - - void provision(); - - bool lockStatusNeoPixel; ///< True if status LED is using the status neopixel - bool lockStatusDotStar; ///< True if status LED is using the status dotstar - bool lockStatusLED; ///< True if status LED is using the built-in LED - float status_pixel_brightness = - STATUS_PIXEL_BRIGHTNESS_DEFAULT; ///< Global status pixel's brightness - ///< (from 0.0 to 1.0) - - virtual void set_user_key(); - virtual void set_ssid_pass(const char *ssid, const char *ssidPassword); - virtual void set_ssid_pass(); - virtual bool check_valid_ssid(); - - virtual void _connect(); - virtual void _disconnect(); - void connect(); - void disconnect(); - - virtual void getMacAddr(); - virtual void setupMQTTClient(const char *clientID); - - virtual ws_status_t networkStatus(); - ws_board_status_t getBoardStatus(); - - bool generateDeviceUID(); - bool generateWSTopics(); - bool generateWSErrorTopics(); - - // Registration API - bool registerBoard(); - bool encodePubRegistrationReq(); - void decodeRegistrationResp(char *data, uint16_t len); - void pollRegistrationResp(); - // Configuration API - void publishPinConfigComplete(); - - // run() loop - ws_status_t run(); - void processPackets(); - void publish(const char *topic, uint8_t *payload, uint16_t bLen, - uint8_t qos = 0); - - // Networking helpers - void pingBroker(); - void runNetFSM(); - - // WDT helpers - void enableWDT(int timeoutMS = 0); - void feedWDT(); - - // Error handling helpers - void haltError(String error, - ws_led_status_t ledStatusColor = WS_LED_STATUS_ERROR_RUNTIME); - void errorWriteHang(String error); - - // MQTT topic callbacks // - // Decodes a signal message - bool decodeSignalMsg( - wippersnapper_signal_v1_CreateSignalRequest *encodedSignalMsg); - - // Encodes a pin event message - bool - encodePinEvent(wippersnapper_signal_v1_CreateSignalRequest *outgoingSignalMsg, - uint8_t pinName, int pinVal); - - // Pin configure message - bool configureDigitalPinReq(wippersnapper_pin_v1_ConfigurePinRequest *pinMsg); - bool configAnalogInPinReq(wippersnapper_pin_v1_ConfigurePinRequest *pinMsg); - - // I2C - std::vector - i2cComponents; ///< Vector containing all I2C components - WipperSnapper_Component_I2C *_i2cPort0 = - NULL; ///< WipperSnapper I2C Component for I2C port #0 - WipperSnapper_Component_I2C *_i2cPort1 = - NULL; ///< WipperSnapper I2C Component for I2C port #1 - bool _isI2CPort0Init = - false; ///< True if I2C port 0 has been initialized, False otherwise. - bool _isI2CPort1Init = - false; ///< True if I2C port 1 has been initialized, False otherwise. - - uint8_t _buffer[WS_MQTT_MAX_PAYLOAD_SIZE]; /*!< Shared buffer to save callback - payload */ - uint8_t - _buffer_outgoing[WS_MQTT_MAX_PAYLOAD_SIZE]; /*!< buffer which contains - outgoing payload data */ - uint16_t bufSize; /*!< Length of data inside buffer */ - - ws_board_status_t _boardStatus = - WS_BOARD_DEF_IDLE; ///< Hardware's registration status - - // TODO: We really should look at making these static definitions, not dynamic - // to free up space on the heap - Wippersnapper_DigitalGPIO *_digitalGPIO; ///< Instance of digital gpio class - Wippersnapper_AnalogIO *_analogIO; ///< Instance of analog io class - Wippersnapper_FS *_fileSystem; ///< Instance of Filesystem (native USB) - WipperSnapper_LittleFS - *_littleFS; ///< Instance of LittleFS Filesystem (non-native USB) -#ifdef USE_DISPLAY - ws_display_driver *_display = nullptr; ///< Instance of display driver class - ws_display_ui_helper *_ui_helper = - nullptr; ///< Instance of display UI helper class -#endif - ws_pixels *_ws_pixelsComponent; ///< ptr to instance of ws_pixels class - ws_pwm *_pwmComponent; ///< Instance of pwm class - ws_servo *_servoComponent; ///< Instance of servo class - ws_ds18x20 *_ds18x20Component; ///< Instance of DS18x20 class - ws_uart *_uartComponent; ///< Instance of UART class - - // TODO: does this really need to be global? - uint8_t _macAddr[6]; /*!< Unique network iface identifier */ - char sUID[13]; /*!< Unique network iface identifier */ - const char *_boardId; /*!< Adafruit IO+ board string */ - Adafruit_MQTT *_mqtt; /*!< Reference to Adafruit_MQTT, _mqtt. */ - - secretsConfig _config; /*!< Wippersnapper secrets.json as a struct. */ - - // TODO: Does this need to be within this class? - int32_t totalDigitalPins; /*!< Total number of digital-input capable pins */ - - char *_topic_description = NULL; /*!< MQTT topic for the device description */ - char *_topic_signal_device = NULL; /*!< Device->Wprsnpr messages */ - char *_topic_signal_i2c_brkr = NULL; /*!< Topic carries messages from a device - to a broker. */ - char *_topic_signal_i2c_device = NULL; /*!< Topic carries messages from a - broker to a device. */ - char *_topic_signal_servo_brkr = NULL; /*!< Topic carries messages from a - device to a broker. */ - char *_topic_signal_servo_device = NULL; /*!< Topic carries messages from a - broker to a device. */ - char *_topic_signal_pwm_brkr = - NULL; /*!< Topic carries PWM messages from a device to a broker. */ - char *_topic_signal_pwm_device = - NULL; /*!< Topic carries PWM messages from a broker to a device. */ - char *_topic_signal_ds18_brkr = NULL; /*!< Topic carries ds18x20 messages from - a device to a broker. */ - char *_topic_signal_ds18_device = NULL; /*!< Topic carries ds18x20 messages - from a broker to a device. */ - char *_topic_signal_pixels_brkr = NULL; /*!< Topic carries pixel messages */ - char *_topic_signal_pixels_device = NULL; /*!< Topic carries pixel messages */ - char *_topic_signal_uart_brkr = NULL; /*!< Topic carries UART messages */ - char *_topic_signal_uart_device = NULL; /*!< Topic carries UART messages */ - - wippersnapper_signal_v1_CreateSignalRequest - _incomingSignalMsg; /*!< Incoming signal message from broker */ - wippersnapper_signal_v1_I2CRequest msgSignalI2C = - wippersnapper_signal_v1_I2CRequest_init_zero; ///< I2C request wrapper - ///< message - - // ds signal msg - wippersnapper_signal_v1_Ds18x20Request msgSignalDS = - wippersnapper_signal_v1_Ds18x20Request_init_zero; ///< DS request message - ///< wrapper - - // servo message - wippersnapper_signal_v1_ServoRequest - msgServo; ///< ServoRequest wrapper message - wippersnapper_signal_v1_PWMRequest msgPWM = - wippersnapper_signal_v1_PWMRequest_init_zero; ///< PWM request wrapper - ///< message. - - // pixels signal message - wippersnapper_signal_v1_PixelsRequest - msgPixels; ///< PixelsRequest wrapper message - - wippersnapper_signal_v1_UARTRequest - msgSignalUART; ///< UARTReq wrapper message - - char *throttleMessage; /*!< Pointer to throttle message data. */ - int throttleTime; /*!< Total amount of time to throttle the device, in - milliseconds. */ - - bool pinCfgCompleted = false; /*!< Did initial pin sync complete? */ - -// enable LEDC if esp32 -#ifdef ARDUINO_ARCH_ESP32 - ws_ledc *_ledc = nullptr; ///< Pointer to LEDC object -#endif - -private: - void _init(); - -protected: - ws_status_t _status = WS_IDLE; /*!< Adafruit IO connection status */ - uint32_t _last_mqtt_connect = 0; /*!< Previous time when client connected to - Adafruit IO, in milliseconds. */ - uint32_t _prv_ping = 0; /*!< Previous time when client pinged Adafruit IO's - MQTT broker, in milliseconds. */ - uint32_t _prvKATBlink = 0; /*!< Previous time when client pinged Adafruit IO's - MQTT broker, in milliseconds. */ - - // Device information - const char *_deviceId; /*!< Adafruit IO+ device identifier string */ - char *_device_uid; /*!< Unique device identifier */ - - // MQTT topics - char *_topic_description_status = - NULL; /*!< MQTT subtopic carrying the description - status resp. from the broker */ - char *_topic_description_status_complete = NULL; /*!< MQTT topic carrying the - ACK signal from the device to the - broker after registration */ - char *_topic_device_pin_config_complete = - NULL; /*!< MQTT topic carrying the ACK signal - from the device to the broker after - hardware configuration */ - char *_topic_signal_brkr = NULL; /*!< Wprsnpr->Device messages */ - char *_err_topic = NULL; /*!< Adafruit IO MQTT error message topic. */ - char *_throttle_topic = NULL; /*!< Adafruit IO MQTT throttle message topic. */ - - Adafruit_MQTT_Subscribe *_topic_description_sub; /*!< Subscription callback - for registration topic. */ - Adafruit_MQTT_Publish *_topic_signal_device_pub; /*!< Subscription callback - for D2C signal topic. */ - Adafruit_MQTT_Subscribe *_topic_signal_brkr_sub; /*!< Subscription callback - for C2D signal topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_i2c_sub; /*!< Subscription callback for I2C topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_servo_sub; /*!< Subscription callback for servo topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_pwm_sub; /*!< Subscription callback for pwm topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_ds18_sub; /*!< Subscribes to signal's ds18x20 topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_pixels_sub; /*!< Subscribes to pixel device topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_uart_sub; /*!< Subscribes to signal's UART topic. */ - - Adafruit_MQTT_Subscribe - *_err_sub; /*!< Subscription to Adafruit IO Error topic. */ - Adafruit_MQTT_Subscribe - *_throttle_sub; /*!< Subscription to Adafruit IO Throttle topic. */ - - wippersnapper_signal_v1_CreateSignalRequest - _outgoingSignalMsg; /*!< Outgoing signal message from device */ -}; -extern Wippersnapper WS; ///< Global member variable for callbacks - -#endif // ADAFRUIT_WIPPERSNAPPER_H +/*! + * @file Wippersnapper.h + * + * This is the documentation for Adafruit's Wippersnapper firmware for the + * Arduino platform. It is designed specifically to work with + * Adafruit IO Wippersnapper IoT platform. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2020-2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#ifndef WIPPERSNAPPER_H +#define WIPPERSNAPPER_H + +// Cpp STD +#include + +// Nanopb dependencies +#include +#include +#include +#include + +#include // description.proto +#include // signal.proto + +// External libraries +#include "Adafruit_MQTT.h" // MQTT Client +#include "Adafruit_SleepyDog.h" // Watchdog +#include "Arduino.h" // Wiring +#include // SPI + +// Wippersnapper API Helpers +#include "Wippersnapper_Boards.h" +#include "components/statusLED/Wippersnapper_StatusLED.h" +#include "provisioning/ConfigJson.h" + +#define WS_DEBUG ///< Define to enable debugging to serial terminal +#define WS_PRINTER Serial ///< Where debug messages will be printed + +// Define actual debug output functions when necessary. +#ifdef WS_DEBUG +#define WS_DEBUG_PRINT(...) \ + { WS_PRINTER.print(__VA_ARGS__); } ///< Prints debug output. +#define WS_DEBUG_PRINTLN(...) \ + { WS_PRINTER.println(__VA_ARGS__); } ///< Prints line from debug output. +#define WS_DEBUG_PRINTHEX(...) \ + { WS_PRINTER.print(__VA_ARGS__, HEX); } ///< Prints debug output. +#else +#define WS_DEBUG_PRINT(...) \ + {} ///< Prints debug output +#define WS_DEBUG_PRINTLN(...) \ + {} ///< Prints line from debug output. +#endif + +// Wippersnapper pb helpers +#include + +// Wippersnapper components +#include "components/analogIO/Wippersnapper_AnalogIO.h" +#include "components/digitalIO/Wippersnapper_DigitalGPIO.h" +#include "components/i2c/WipperSnapper_I2C.h" + +// LEDC-Manager, ESP32-only +#ifdef ARDUINO_ARCH_ESP32 +#include "components/ledc/ws_ledc.h" +#endif + +// Display +#ifdef USE_DISPLAY +#include "display/ws_display_driver.h" +#include "display/ws_display_ui_helper.h" +#endif + +#include "components/ds18x20/ws_ds18x20.h" +#include "components/pixels/ws_pixels.h" +#include "components/pwm/ws_pwm.h" +#include "components/servo/ws_servo.h" +#include "components/uart/ws_uart.h" + +#if defined(USE_TINYUSB) +#include "provisioning/tinyusb/Wippersnapper_FS.h" +#endif + +#if defined(USE_LITTLEFS) +#include "provisioning/littlefs/WipperSnapper_LittleFS.h" +#endif + +#define WS_VERSION \ + "1.0.0-beta.85" ///< WipperSnapper app. version (semver-formatted) + +// Reserved Adafruit IO MQTT topics +#define TOPIC_IO_THROTTLE "/throttle" ///< Adafruit IO Throttle MQTT Topic +#define TOPIC_IO_ERRORS "/errors" ///< Adafruit IO Error MQTT Topic + +// Reserved Wippersnapper topics +#define TOPIC_WS "/wprsnpr/" ///< WipperSnapper topic +#define TOPIC_INFO "/info/" ///< Registration sub-topic +#define TOPIC_SIGNALS "/signals/" ///< Signals sub-topic +#define TOPIC_I2C "/i2c" ///< I2C sub-topic +#define MQTT_TOPIC_PIXELS_DEVICE \ + "/signals/device/pixel" ///< Pixels device->broker topic +#define MQTT_TOPIC_PIXELS_BROKER \ + "/signals/broker/pixel" ///< Pixels broker->device topic + +/** Defines the Adafruit IO connection status */ +typedef enum { + WS_IDLE = 0, // Waiting for connection establishement + WS_NET_DISCONNECTED = 1, // Network disconnected + WS_DISCONNECTED = 2, // Disconnected from Adafruit IO + WS_FINGERPRINT_UNKOWN = 3, // Unknown WS_SSL_FINGERPRINT + + WS_NET_CONNECT_FAILED = 10, // Failed to connect to network + WS_CONNECT_FAILED = 11, // Failed to connect to Adafruit IO + WS_FINGERPRINT_INVALID = 12, // Unknown WS_SSL_FINGERPRINT + WS_AUTH_FAILED = 13, // Invalid Adafruit IO login credentials provided. + WS_SSID_INVALID = + 14, // SSID is "" or otherwise invalid, connection not attempted + + WS_NET_CONNECTED = 20, // Connected to Adafruit IO + WS_CONNECTED = 21, // Connected to network + WS_CONNECTED_INSECURE = 22, // Insecurely (non-SSL) connected to network + WS_FINGERPRINT_UNSUPPORTED = 23, // Unsupported WS_SSL_FINGERPRINT + WS_FINGERPRINT_VALID = 24, // Valid WS_SSL_FINGERPRINT + WS_BOARD_DESC_INVALID = 25, // Unable to send board description + WS_BOARD_RESYNC_FAILED = 26 // Board sync failure +} ws_status_t; + +/** Defines the Adafruit IO MQTT broker's connection return codes */ +typedef enum { + WS_MQTT_CONNECTED = 0, // Connected + WS_MQTT_INVALID_PROTOCOL = 1, // Invalid mqtt protocol + WS_MQTT_INVALID_CID = 2, // Client id rejected + WS_MQTT_SERVICE_UNAVALIABLE = 3, // Malformed user/pass + WS_MQTT_INVALID_USER_PASS = 4, // Unauthorized access to resource + WS_MQTT_UNAUTHORIZED = 5, // MQTT service unavailable + WS_MQTT_THROTTLED = 6, // Account throttled + WS_MQTT_BANNED = 7 // Account banned +} ws_mqtt_status_t; + +/** Defines the Wippersnapper client's hardware registration status */ +typedef enum { + WS_BOARD_DEF_IDLE, + WS_BOARD_DEF_SEND_FAILED, + WS_BOARD_DEF_SENT, + WS_BOARD_DEF_OK, + WS_BOARD_DEF_INVALID, + WS_BOARD_DEF_UNSPECIFIED +} ws_board_status_t; + +/** Defines the Wippersnapper client's network status */ +typedef enum { + FSM_NET_IDLE, + FSM_NET_CONNECTED, + FSM_MQTT_CONNECTED, + FSM_NET_CHECK_MQTT, + FSM_NET_CHECK_NETWORK, + FSM_NET_ESTABLISH_NETWORK, + FSM_NET_ESTABLISH_MQTT, +} fsm_net_t; + +#define WS_WDT_TIMEOUT 60000 ///< WDT timeout +/* MQTT Configuration */ +#define WS_KEEPALIVE_INTERVAL_MS \ + 5000 ///< Session keepalive interval time, in milliseconds + +#define WS_MQTT_MAX_PAYLOAD_SIZE \ + 512 ///< MAXIMUM expected payload size, in bytes + +class Wippersnapper_DigitalGPIO; +class Wippersnapper_AnalogIO; +class Wippersnapper_FS; +class WipperSnapper_LittleFS; +#ifdef USE_DISPLAY +class ws_display_driver; +class ws_display_ui_helper; +#endif +#ifdef ARDUINO_ARCH_ESP32 +class ws_ledc; +#endif +class WipperSnapper_Component_I2C; +class ws_servo; +class ws_pwm; +class ws_ds18x20; +class ws_pixels; +class ws_uart; + +/**************************************************************************/ +/*! + @brief Class that provides storage and functions for the Adafruit IO + Wippersnapper interface. +*/ +/**************************************************************************/ +class Wippersnapper { +public: + Wippersnapper(); + virtual ~Wippersnapper(); + + void provision(); + + bool lockStatusNeoPixel; ///< True if status LED is using the status neopixel + bool lockStatusDotStar; ///< True if status LED is using the status dotstar + bool lockStatusLED; ///< True if status LED is using the built-in LED + float status_pixel_brightness = + STATUS_PIXEL_BRIGHTNESS_DEFAULT; ///< Global status pixel's brightness + ///< (from 0.0 to 1.0) + + virtual void set_user_key(); + virtual void set_ssid_pass(const char *ssid, const char *ssidPassword); + virtual void set_ssid_pass(); + virtual bool check_valid_ssid(); + + virtual void _connect(); + virtual void _disconnect(); + void connect(); + void disconnect(); + + virtual void getMacAddr(); + virtual void getRSSI(); + virtual void setupMQTTClient(const char *clientID); + + virtual ws_status_t networkStatus(); + ws_board_status_t getBoardStatus(); + + bool generateDeviceUID(); + bool generateWSTopics(); + bool generateWSErrorTopics(); + + // Registration API + bool registerBoard(); + bool encodePubRegistrationReq(); + void decodeRegistrationResp(char *data, uint16_t len); + void pollRegistrationResp(); + // Configuration API + void publishPinConfigComplete(); + + // run() loop + ws_status_t run(); + void processPackets(); + void publish(const char *topic, uint8_t *payload, uint16_t bLen, + uint8_t qos = 0); + + // Networking helpers + void pingBroker(); + void runNetFSM(); + + // WDT helpers + void enableWDT(int timeoutMS = 0); + void feedWDT(); + + // Error handling helpers + void haltError(String error, + ws_led_status_t ledStatusColor = WS_LED_STATUS_ERROR_RUNTIME); + void errorWriteHang(String error); + + // MQTT topic callbacks // + // Decodes a signal message + bool decodeSignalMsg( + wippersnapper_signal_v1_CreateSignalRequest *encodedSignalMsg); + + // Encodes a pin event message + bool + encodePinEvent(wippersnapper_signal_v1_CreateSignalRequest *outgoingSignalMsg, + uint8_t pinName, int pinVal); + + // Pin configure message + bool configureDigitalPinReq(wippersnapper_pin_v1_ConfigurePinRequest *pinMsg); + bool configAnalogInPinReq(wippersnapper_pin_v1_ConfigurePinRequest *pinMsg); + + // I2C + std::vector + i2cComponents; ///< Vector containing all I2C components + WipperSnapper_Component_I2C *_i2cPort0 = + NULL; ///< WipperSnapper I2C Component for I2C port #0 + WipperSnapper_Component_I2C *_i2cPort1 = + NULL; ///< WipperSnapper I2C Component for I2C port #1 + bool _isI2CPort0Init = + false; ///< True if I2C port 0 has been initialized, False otherwise. + bool _isI2CPort1Init = + false; ///< True if I2C port 1 has been initialized, False otherwise. + + uint8_t _buffer[WS_MQTT_MAX_PAYLOAD_SIZE]; /*!< Shared buffer to save callback + payload */ + uint8_t + _buffer_outgoing[WS_MQTT_MAX_PAYLOAD_SIZE]; /*!< buffer which contains + outgoing payload data */ + uint16_t bufSize; /*!< Length of data inside buffer */ + + ws_board_status_t _boardStatus = + WS_BOARD_DEF_IDLE; ///< Hardware's registration status + + // TODO: We really should look at making these static definitions, not dynamic + // to free up space on the heap + Wippersnapper_DigitalGPIO *_digitalGPIO; ///< Instance of digital gpio class + Wippersnapper_AnalogIO *_analogIO; ///< Instance of analog io class + Wippersnapper_FS *_fileSystem; ///< Instance of Filesystem (native USB) + WipperSnapper_LittleFS + *_littleFS; ///< Instance of LittleFS Filesystem (non-native USB) +#ifdef USE_DISPLAY + ws_display_driver *_display = nullptr; ///< Instance of display driver class + ws_display_ui_helper *_ui_helper = + nullptr; ///< Instance of display UI helper class +#endif + ws_pixels *_ws_pixelsComponent; ///< ptr to instance of ws_pixels class + ws_pwm *_pwmComponent; ///< Instance of pwm class + ws_servo *_servoComponent; ///< Instance of servo class + ws_ds18x20 *_ds18x20Component; ///< Instance of DS18x20 class + ws_uart *_uartComponent; ///< Instance of UART class + + // TODO: does this really need to be global? + int32_t _RSSI = 0; ///< RSSI value of the network connection + uint8_t _macAddr[6]; /*!< Unique network iface identifier */ + char sUID[13]; /*!< Unique network iface identifier */ + const char *_boardId; /*!< Adafruit IO+ board string */ + Adafruit_MQTT *_mqtt; /*!< Reference to Adafruit_MQTT, _mqtt. */ + + secretsConfig _config; /*!< Wippersnapper secrets.json as a struct. */ + + // TODO: Does this need to be within this class? + int32_t totalDigitalPins; /*!< Total number of digital-input capable pins */ + + char *_topic_description = NULL; /*!< MQTT topic for the device description */ + char *_topic_signal_device = NULL; /*!< Device->Wprsnpr messages */ + char *_topic_signal_i2c_brkr = NULL; /*!< Topic carries messages from a device + to a broker. */ + char *_topic_signal_i2c_device = NULL; /*!< Topic carries messages from a + broker to a device. */ + char *_topic_signal_servo_brkr = NULL; /*!< Topic carries messages from a + device to a broker. */ + char *_topic_signal_servo_device = NULL; /*!< Topic carries messages from a + broker to a device. */ + char *_topic_signal_pwm_brkr = + NULL; /*!< Topic carries PWM messages from a device to a broker. */ + char *_topic_signal_pwm_device = + NULL; /*!< Topic carries PWM messages from a broker to a device. */ + char *_topic_signal_ds18_brkr = NULL; /*!< Topic carries ds18x20 messages from + a device to a broker. */ + char *_topic_signal_ds18_device = NULL; /*!< Topic carries ds18x20 messages + from a broker to a device. */ + char *_topic_signal_pixels_brkr = NULL; /*!< Topic carries pixel messages */ + char *_topic_signal_pixels_device = NULL; /*!< Topic carries pixel messages */ + char *_topic_signal_uart_brkr = NULL; /*!< Topic carries UART messages */ + char *_topic_signal_uart_device = NULL; /*!< Topic carries UART messages */ + + wippersnapper_signal_v1_CreateSignalRequest + _incomingSignalMsg; /*!< Incoming signal message from broker */ + wippersnapper_signal_v1_I2CRequest msgSignalI2C = + wippersnapper_signal_v1_I2CRequest_init_zero; ///< I2C request wrapper + ///< message + + // ds signal msg + wippersnapper_signal_v1_Ds18x20Request msgSignalDS = + wippersnapper_signal_v1_Ds18x20Request_init_zero; ///< DS request message + ///< wrapper + + // servo message + wippersnapper_signal_v1_ServoRequest + msgServo; ///< ServoRequest wrapper message + wippersnapper_signal_v1_PWMRequest msgPWM = + wippersnapper_signal_v1_PWMRequest_init_zero; ///< PWM request wrapper + ///< message. + + // pixels signal message + wippersnapper_signal_v1_PixelsRequest + msgPixels; ///< PixelsRequest wrapper message + + wippersnapper_signal_v1_UARTRequest + msgSignalUART; ///< UARTReq wrapper message + + char *throttleMessage; /*!< Pointer to throttle message data. */ + int throttleTime; /*!< Total amount of time to throttle the device, in + milliseconds. */ + + bool pinCfgCompleted = false; /*!< Did initial pin sync complete? */ + +// enable LEDC if esp32 +#ifdef ARDUINO_ARCH_ESP32 + ws_ledc *_ledc = nullptr; ///< Pointer to LEDC object +#endif + +private: + void _init(); + +protected: + ws_status_t _status = WS_IDLE; /*!< Adafruit IO connection status */ + uint32_t _last_mqtt_connect = 0; /*!< Previous time when client connected to + Adafruit IO, in milliseconds. */ + uint32_t _prv_ping = 0; /*!< Previous time when client pinged Adafruit IO's + MQTT broker, in milliseconds. */ + uint32_t _prvKATBlink = 0; /*!< Previous time when client pinged Adafruit IO's + MQTT broker, in milliseconds. */ + + // Device information + const char *_deviceId; /*!< Adafruit IO+ device identifier string */ + char *_device_uid; /*!< Unique device identifier */ + + // MQTT topics + char *_topic_description_status = + NULL; /*!< MQTT subtopic carrying the description + status resp. from the broker */ + char *_topic_description_status_complete = NULL; /*!< MQTT topic carrying the + ACK signal from the device to the + broker after registration */ + char *_topic_device_pin_config_complete = + NULL; /*!< MQTT topic carrying the ACK signal + from the device to the broker after + hardware configuration */ + char *_topic_signal_brkr = NULL; /*!< Wprsnpr->Device messages */ + char *_err_topic = NULL; /*!< Adafruit IO MQTT error message topic. */ + char *_throttle_topic = NULL; /*!< Adafruit IO MQTT throttle message topic. */ + + Adafruit_MQTT_Subscribe *_topic_description_sub; /*!< Subscription callback + for registration topic. */ + Adafruit_MQTT_Publish *_topic_signal_device_pub; /*!< Subscription callback + for D2C signal topic. */ + Adafruit_MQTT_Subscribe *_topic_signal_brkr_sub; /*!< Subscription callback + for C2D signal topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_i2c_sub; /*!< Subscription callback for I2C topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_servo_sub; /*!< Subscription callback for servo topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_pwm_sub; /*!< Subscription callback for pwm topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_ds18_sub; /*!< Subscribes to signal's ds18x20 topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_pixels_sub; /*!< Subscribes to pixel device topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_uart_sub; /*!< Subscribes to signal's UART topic. */ + + Adafruit_MQTT_Subscribe + *_err_sub; /*!< Subscription to Adafruit IO Error topic. */ + Adafruit_MQTT_Subscribe + *_throttle_sub; /*!< Subscription to Adafruit IO Throttle topic. */ + + wippersnapper_signal_v1_CreateSignalRequest + _outgoingSignalMsg; /*!< Outgoing signal message from device */ +}; +extern Wippersnapper WS; ///< Global member variable for callbacks + +#endif // ADAFRUIT_WIPPERSNAPPER_H diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index dbd8bc2f2..b7665a3f5 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -1,299 +1,310 @@ -/*! - * @file Wippersnapper_AIRLIFT.h - * - * This is a driver for using the Adafruit AirLift - * ESP32 Co-Processor's network interface with Wippersnapper. - * - * The ESP32 AirLift uses SPI to communicate. Three lines (CS, ACK, RST) are - * required to communicate with the ESP32 AirLift. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries. - * - * MIT license, all text here must be included in any redistribution. - * - */ - -#ifndef WIPPERSNAPPER_AIRLIFT_H -#define WIPPERSNAPPER_AIRLIFT_H - -#include "Adafruit_MQTT.h" -#include "Adafruit_MQTT_Client.h" -#include "Arduino.h" -#include "SPI.h" -#include "WiFiNINA.h" -#include "Wippersnapper.h" - -#define NINAFWVER \ - "1.6.0" /*!< min. nina-fw version compatible with this library. */ - -#define SPIWIFI SPI /*!< Instance of SPI interface used by an AirLift. */ - -extern Wippersnapper WS; -/****************************************************************************/ -/*! - @brief Class for using the AirLift Co-Processor network iface. -*/ -/****************************************************************************/ -class Wippersnapper_AIRLIFT : public Wippersnapper { - -public: - /**************************************************************************/ - /*! - @brief Initializes the Adafruit IO class for AirLift devices. - */ - /**************************************************************************/ - Wippersnapper_AIRLIFT() : Wippersnapper() { - _ssPin = 10; - _ackPin = 7; - _rstPin = 5; - _gpio0Pin = -1; - _wifi = &SPIWIFI; - _ssid = 0; - _pass = 0; - _mqtt_client = new WiFiSSLClient; - - // setup ESP32 co-processor pins during init. - WiFi.setPins(_ssPin, _ackPin, _rstPin, _gpio0Pin, _wifi); - } - - /**************************************************************************/ - /*! - @brief Destructor for the Adafruit IO AirLift class. - */ - /**************************************************************************/ - ~Wippersnapper_AIRLIFT() { - if (_mqtt) - delete _mqtt; - } - - /**********************************************************/ - /*! - @brief Sets the WiFi client's ssid and password. - @param ssid - Wireless network's SSID. - @param ssidPassword - Wireless network's password. - */ - /**********************************************************/ - void set_ssid_pass(const char *ssid, const char *ssidPassword) { - _ssid = ssid; - _pass = ssidPassword; - } - - /**********************************************************/ - /*! - @brief Sets the WiFi client's ssid and password from the - secrets.json provisioning file. - */ - /**********************************************************/ - void set_ssid_pass() { - _ssid = WS._config.network.ssid; - _pass = WS._config.network.pass; - } - - /***********************************************************/ - /*! - @brief Performs a scan of local WiFi networks. - @returns True if `_network_ssid` is found, False otherwise. - */ - /***********************************************************/ - bool check_valid_ssid() { - // Disconnect WiFi from an AP if it was previously connected - WiFi.disconnect(); - delay(100); - - // Perform a network scan - int n = WiFi.scanNetworks(); - if (n == 0) { - WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!"); - return false; - } - - // Was the network within secrets.json found? - for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i)) == 0) - return true; - } - - // User-set network not found, print scan results to serial console - WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); - WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); - for (int i = 0; i < n; ++i) { - WS_DEBUG_PRINT(WiFi.SSID(i)); - WS_DEBUG_PRINT(" "); - WS_DEBUG_PRINT(WiFi.RSSI(i)); - WS_DEBUG_PRINTLN("dB"); - } - - return false; - } - - /********************************************************/ - /*! - @brief Sets the WiFi client. - @param wifi - Instance of SPIClass. - */ - /********************************************************/ - void set_wifi(SPIClass *wifi) { - _wifi = wifi; - _mqtt_client = new WiFiSSLClient; - } - - /********************************************************/ - /*! - @brief Configures ESP32 "AirLift" pins. - @param ssPin - ESP32 S.S. pin. - @param ackPin - ESP32 ACK pin. - @param rstPin - ESP32 RST pin. - @param gpio0Pin - ESP32 GPIO0 pin. - */ - /********************************************************/ - void set_airlift_pins(int ssPin, int ackPin, int rstPin, int gpio0Pin) { - _ssPin = ssPin; - _ackPin = ackPin; - _rstPin = rstPin; - _gpio0Pin = gpio0Pin; - } - - /********************************************************/ - /*! - @brief Checks the version of an ESP32 module running - nina-fw. - @returns True if matches min. required to run - WipperSnapper, False otherwise. - */ - /********************************************************/ - bool firmwareCheck() { - _fv = WiFi.firmwareVersion(); - if (_fv < NINAFWVER) - return false; - return true; - } - - /********************************************************/ - /*! - @brief Gets the ESP32's unique client identifier. - @note For the ESP32, the UID is the MAC address. - */ - /********************************************************/ - void getMacAddr() { - uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - WiFi.macAddress(mac); - memcpy(WS._macAddr, mac, sizeof(mac)); - } - - /********************************************************/ - /*! - @brief Initializes the MQTT client. - @param clientID - MQTT client identifier - */ - /********************************************************/ - void setupMQTTClient(const char *clientID) { - 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); - } - - /********************************************************/ - /*! - @brief Returns the network status of an ESP32 module. - @return ws_status_t - */ - /********************************************************/ - ws_status_t networkStatus() { - switch (WiFi.status()) { - case WL_CONNECTED: - return WS_NET_CONNECTED; - case WL_CONNECT_FAILED: - return WS_NET_CONNECT_FAILED; - case WL_IDLE_STATUS: - return WS_IDLE; - default: - return WS_NET_DISCONNECTED; - } - } - - /*******************************************************************/ - /*! - @brief Returns the type of network connection used by Wippersnapper - @return AIRLIFT - */ - /*******************************************************************/ - const char *connectionType() { return "AIRLIFT"; } - -protected: - const char *_ssid; /*!< Network SSID. */ - const char *_pass; /*!< Network password. */ - String _fv; /*!< nina-fw firmware version. */ - int _ssPin = -1; /*!< SPI S.S. pin. */ - int _ackPin = -1; /*!< SPI ACK pin. */ - int _rstPin = -1; /*!< SPI RST pin. */ - int _gpio0Pin = -1; /*!< SPI GPIO0 pin, unused. */ - WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */ - SPIClass *_wifi; /*!< Instance of the SPI bus used by the AirLift. */ - - /**************************************************************************/ - /*! - @brief Establishes a connection with the wireless network. - */ - /**************************************************************************/ - void _connect() { - if (strlen(_ssid) == 0) { - _status = WS_SSID_INVALID; - } else { - - // validate co-processor is physically connected connection - if (WiFi.status() == WL_NO_MODULE) { - WS_DEBUG_PRINT("No ESP32 module detected!"); - return; - } - - // validate co-processor's firmware version - if (!firmwareCheck()) - WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the " - "latest version."); - - // disconnect from possible previous connection - _disconnect(); - - // reset the esp32 if possible - if (_rstPin != -1) { - WS_DEBUG("Resetting ESP32..."); - pinMode(_rstPin, OUTPUT); - digitalWrite(_rstPin, LOW); - delay(10); - digitalWrite(_rstPin, HIGH); - delay(10); - } - // wait for the ESP32 to boot - delay(1000); - - WS_DEBUG_PRINT("Connecting to "); - WS_DEBUG_PRINTLN(_ssid); - WiFi.begin(_ssid, _pass); - _status = WS_NET_DISCONNECTED; - } - } - - /**************************************************************************/ - /*! - @brief Disconnects from the wireless network. - */ - /**************************************************************************/ - void _disconnect() { - WiFi.disconnect(); - delay(500); - } -}; - +/*! + * @file Wippersnapper_AIRLIFT.h + * + * This is a driver for using the Adafruit AirLift + * ESP32 Co-Processor's network interface with Wippersnapper. + * + * The ESP32 AirLift uses SPI to communicate. Three lines (CS, ACK, RST) are + * required to communicate with the ESP32 AirLift. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries. + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef WIPPERSNAPPER_AIRLIFT_H +#define WIPPERSNAPPER_AIRLIFT_H + +#include "Adafruit_MQTT.h" +#include "Adafruit_MQTT_Client.h" +#include "Arduino.h" +#include "SPI.h" +#include "WiFiNINA.h" +#include "Wippersnapper.h" + +#define NINAFWVER \ + "1.6.0" /*!< min. nina-fw version compatible with this library. */ + +#define SPIWIFI SPI /*!< Instance of SPI interface used by an AirLift. */ + +extern Wippersnapper WS; +/****************************************************************************/ +/*! + @brief Class for using the AirLift Co-Processor network iface. +*/ +/****************************************************************************/ +class Wippersnapper_AIRLIFT : public Wippersnapper { + +public: + /**************************************************************************/ + /*! + @brief Initializes the Adafruit IO class for AirLift devices. + */ + /**************************************************************************/ + Wippersnapper_AIRLIFT() : Wippersnapper() { + _ssPin = 10; + _ackPin = 7; + _rstPin = 5; + _gpio0Pin = -1; + _wifi = &SPIWIFI; + _ssid = 0; + _pass = 0; + _mqtt_client = new WiFiSSLClient; + + // setup ESP32 co-processor pins during init. + WiFi.setPins(_ssPin, _ackPin, _rstPin, _gpio0Pin, _wifi); + } + + /**************************************************************************/ + /*! + @brief Destructor for the Adafruit IO AirLift class. + */ + /**************************************************************************/ + ~Wippersnapper_AIRLIFT() { + if (_mqtt) + delete _mqtt; + } + + /**********************************************************/ + /*! + @brief Sets the WiFi client's ssid and password. + @param ssid + Wireless network's SSID. + @param ssidPassword + Wireless network's password. + */ + /**********************************************************/ + void set_ssid_pass(const char *ssid, const char *ssidPassword) { + _ssid = ssid; + _pass = ssidPassword; + } + + /**********************************************************/ + /*! + @brief Sets the WiFi client's ssid and password from the + secrets.json provisioning file. + */ + /**********************************************************/ + void set_ssid_pass() { + _ssid = WS._config.network.ssid; + _pass = WS._config.network.pass; + } + + /***********************************************************/ + /*! + @brief Performs a scan of local WiFi networks. + @returns True if `_network_ssid` is found, False otherwise. + */ + /***********************************************************/ + bool check_valid_ssid() { + // Disconnect WiFi from an AP if it was previously connected + WiFi.disconnect(); + delay(100); + + // Perform a network scan + int n = WiFi.scanNetworks(); + if (n == 0) { + WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!"); + return false; + } + + // Was the network within secrets.json found? + for (int i = 0; i < n; ++i) { + if (strcmp(_ssid, WiFi.SSID(i)) == 0) + WS._RSSI = WiFi.RSSI(i); + return true; + } + + // User-set network not found, print scan results to serial console + WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); + WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); + for (int i = 0; i < n; ++i) { + WS_DEBUG_PRINT(WiFi.SSID(i)); + WS_DEBUG_PRINT(" "); + WS_DEBUG_PRINT(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN("dB"); + } + + return false; + } + + /********************************************************/ + /*! + @brief Sets the WiFi client. + @param wifi + Instance of SPIClass. + */ + /********************************************************/ + void set_wifi(SPIClass *wifi) { + _wifi = wifi; + _mqtt_client = new WiFiSSLClient; + } + + /********************************************************/ + /*! + @brief Configures ESP32 "AirLift" pins. + @param ssPin + ESP32 S.S. pin. + @param ackPin + ESP32 ACK pin. + @param rstPin + ESP32 RST pin. + @param gpio0Pin + ESP32 GPIO0 pin. + */ + /********************************************************/ + void set_airlift_pins(int ssPin, int ackPin, int rstPin, int gpio0Pin) { + _ssPin = ssPin; + _ackPin = ackPin; + _rstPin = rstPin; + _gpio0Pin = gpio0Pin; + } + + /********************************************************/ + /*! + @brief Checks the version of an ESP32 module running + nina-fw. + @returns True if matches min. required to run + WipperSnapper, False otherwise. + */ + /********************************************************/ + bool firmwareCheck() { + _fv = WiFi.firmwareVersion(); + if (_fv < NINAFWVER) + return false; + return true; + } + + /********************************************************/ + /*! + @brief Gets the ESP32's unique client identifier. + @note For the ESP32, the UID is the MAC address. + */ + /********************************************************/ + void getMacAddr() { + uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + WiFi.macAddress(mac); + memcpy(WS._macAddr, mac, sizeof(mac)); + } + + /********************************************************/ + /*! + @brief Gets the current network RSSI value + */ + /********************************************************/ + void getRSSI() { + // test if this fails when disconnected or returns something sensible + WS._RSSI = WiFi.RSSI(); + } + + /********************************************************/ + /*! + @brief Initializes the MQTT client. + @param clientID + MQTT client identifier + */ + /********************************************************/ + void setupMQTTClient(const char *clientID) { + 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); + } + + /********************************************************/ + /*! + @brief Returns the network status of an ESP32 module. + @return ws_status_t + */ + /********************************************************/ + ws_status_t networkStatus() { + switch (WiFi.status()) { + case WL_CONNECTED: + return WS_NET_CONNECTED; + case WL_CONNECT_FAILED: + return WS_NET_CONNECT_FAILED; + case WL_IDLE_STATUS: + return WS_IDLE; + default: + return WS_NET_DISCONNECTED; + } + } + + /*******************************************************************/ + /*! + @brief Returns the type of network connection used by Wippersnapper + @return AIRLIFT + */ + /*******************************************************************/ + const char *connectionType() { return "AIRLIFT"; } + +protected: + const char *_ssid; /*!< Network SSID. */ + const char *_pass; /*!< Network password. */ + String _fv; /*!< nina-fw firmware version. */ + int _ssPin = -1; /*!< SPI S.S. pin. */ + int _ackPin = -1; /*!< SPI ACK pin. */ + int _rstPin = -1; /*!< SPI RST pin. */ + int _gpio0Pin = -1; /*!< SPI GPIO0 pin, unused. */ + WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */ + SPIClass *_wifi; /*!< Instance of the SPI bus used by the AirLift. */ + + /**************************************************************************/ + /*! + @brief Establishes a connection with the wireless network. + */ + /**************************************************************************/ + void _connect() { + if (strlen(_ssid) == 0) { + _status = WS_SSID_INVALID; + } else { + + // validate co-processor is physically connected connection + if (WiFi.status() == WL_NO_MODULE) { + WS_DEBUG_PRINT("No ESP32 module detected!"); + return; + } + + // validate co-processor's firmware version + if (!firmwareCheck()) + WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the " + "latest version."); + + // disconnect from possible previous connection + _disconnect(); + + // reset the esp32 if possible + if (_rstPin != -1) { + WS_DEBUG("Resetting ESP32..."); + pinMode(_rstPin, OUTPUT); + digitalWrite(_rstPin, LOW); + delay(10); + digitalWrite(_rstPin, HIGH); + delay(10); + } + // wait for the ESP32 to boot + delay(1000); + + WS_DEBUG_PRINT("Connecting to "); + WS_DEBUG_PRINTLN(_ssid); + WiFi.begin(_ssid, _pass); + _status = WS_NET_DISCONNECTED; + } + } + + /**************************************************************************/ + /*! + @brief Disconnects from the wireless network. + */ + /**************************************************************************/ + void _disconnect() { + WiFi.disconnect(); + delay(500); + } +}; + #endif // WIPPERSNAPPER_AIRLIFT_H \ No newline at end of file diff --git a/src/network_interfaces/Wippersnapper_ESP32.h b/src/network_interfaces/Wippersnapper_ESP32.h index 393488737..b5b674990 100644 --- a/src/network_interfaces/Wippersnapper_ESP32.h +++ b/src/network_interfaces/Wippersnapper_ESP32.h @@ -110,6 +110,7 @@ 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._RSSI = WiFi.RSSI(i); return true; } @@ -138,6 +139,16 @@ class Wippersnapper_ESP32 : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + */ + /********************************************************/ + void getRSSI() { + // test if this fails when disconnected or returns something sensible + WS._RSSI = WiFi.RSSI(); + } + /********************************************************/ /*! @brief Initializes the MQTT client diff --git a/src/network_interfaces/Wippersnapper_ESP8266.h b/src/network_interfaces/Wippersnapper_ESP8266.h index 32d2cf264..d824af744 100644 --- a/src/network_interfaces/Wippersnapper_ESP8266.h +++ b/src/network_interfaces/Wippersnapper_ESP8266.h @@ -133,6 +133,7 @@ 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._RSSI = WiFi.RSSI(i); return true; } @@ -161,6 +162,16 @@ class Wippersnapper_ESP8266 : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + */ + /********************************************************/ + void getRSSI() { + // test if this fails when disconnected or returns something sensible + WS._RSSI = WiFi.RSSI(); + } + /*******************************************************************/ /*! @brief Sets up an Adafruit_MQTT_Client diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h index 87de9087f..38211294f 100644 --- a/src/network_interfaces/Wippersnapper_WIFININA.h +++ b/src/network_interfaces/Wippersnapper_WIFININA.h @@ -131,6 +131,7 @@ 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) + WS._RSSI = WiFi.RSSI(i); return true; } @@ -185,6 +186,16 @@ class Wippersnapper_WIFININA : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + */ + /********************************************************/ + void getRSSI() { + // test if this fails when disconnected or returns something sensible + WS._RSSI = 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 c2a8d556c..08bc0b0ab 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -109,7 +109,8 @@ 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) - return true; + WS._RSSI = WiFi.RSSI(i); + return true; } // User-set network not found, print scan results to serial console @@ -137,6 +138,16 @@ class ws_networking_pico : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + */ + /********************************************************/ + void getRSSI() { + // test if this fails when disconnected or returns something sensible + WS._RSSI = WiFi.RSSI(); + } + /********************************************************/ /*! @brief Initializes the MQTT client From ed385b94d4f3b679d4c5de8045fbd751d5f186d3 Mon Sep 17 00:00:00 2001 From: tyeth Date: Wed, 29 May 2024 20:54:38 +0100 Subject: [PATCH 17/54] fix closing bracket on conditional + Clang format --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 3 ++- src/network_interfaces/Wippersnapper_ESP32.h | 3 ++- src/network_interfaces/Wippersnapper_ESP8266.h | 3 ++- src/network_interfaces/Wippersnapper_WIFININA.h | 3 ++- src/network_interfaces/ws_networking_pico.h | 5 +++-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index b7665a3f5..985bfb679 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -115,9 +115,10 @@ 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._RSSI = WiFi.RSSI(i); return true; + } } // User-set network not found, print scan results to serial console diff --git a/src/network_interfaces/Wippersnapper_ESP32.h b/src/network_interfaces/Wippersnapper_ESP32.h index b5b674990..cf1b1c780 100644 --- a/src/network_interfaces/Wippersnapper_ESP32.h +++ b/src/network_interfaces/Wippersnapper_ESP32.h @@ -109,9 +109,10 @@ 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) + if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) { WS._RSSI = WiFi.RSSI(i); return true; + } } // User-set network not found, print scan results to serial console diff --git a/src/network_interfaces/Wippersnapper_ESP8266.h b/src/network_interfaces/Wippersnapper_ESP8266.h index d824af744..51f5df861 100644 --- a/src/network_interfaces/Wippersnapper_ESP8266.h +++ b/src/network_interfaces/Wippersnapper_ESP8266.h @@ -132,9 +132,10 @@ 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) + if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) { WS._RSSI = WiFi.RSSI(i); return true; + } } // User-set network not found, print scan results to serial console diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h index 38211294f..7cbcb1b03 100644 --- a/src/network_interfaces/Wippersnapper_WIFININA.h +++ b/src/network_interfaces/Wippersnapper_WIFININA.h @@ -130,9 +130,10 @@ 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._RSSI = WiFi.RSSI(i); return true; + } } // User-set network not found, print scan results to serial console diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index 08bc0b0ab..5fac941f7 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -108,9 +108,10 @@ 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) + if (strcmp(_ssid, WiFi.SSID(i)) == 0) { WS._RSSI = WiFi.RSSI(i); - return true; + return true; + } } // User-set network not found, print scan results to serial console From eb5770ec788e6f812c19daf95ef95570daf9910d Mon Sep 17 00:00:00 2001 From: tyeth Date: Wed, 29 May 2024 21:35:16 +0100 Subject: [PATCH 18/54] clang-format _RSSI --- src/Wippersnapper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index 76326ef8d..c0ca6b78b 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -314,7 +314,7 @@ class Wippersnapper { ws_uart *_uartComponent; ///< Instance of UART class // TODO: does this really need to be global? - int32_t _RSSI = 0; ///< RSSI value of the network connection + int32_t _RSSI = 0; ///< RSSI value of the network connection uint8_t _macAddr[6]; /*!< Unique network iface identifier */ char sUID[13]; /*!< Unique network iface identifier */ const char *_boardId; /*!< Adafruit IO+ board string */ From d52a4c5b9393ada00e99fd039035c792273eb05e Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Thu, 30 May 2024 18:01:59 +0100 Subject: [PATCH 19/54] WIP: use SS/GPIO0 pins for reset too --- .../Wippersnapper_AIRLIFT.h | 105 +++++++++++++----- .../Wippersnapper_WIFININA.h | 13 ++- src/network_interfaces/ws_networking_pico.h | 3 + 3 files changed, 93 insertions(+), 28 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 985bfb679..477b78dd2 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -28,7 +28,7 @@ #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 SPIWIFI SPI /*!< Instance of SPI interface used by an AirLift. */ @@ -47,10 +47,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; @@ -58,6 +62,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { // setup ESP32 co-processor pins during init. WiFi.setPins(_ssPin, _ackPin, _rstPin, _gpio0Pin, _wifi); + delay(1000); } /**************************************************************************/ @@ -176,9 +181,31 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { /********************************************************/ bool firmwareCheck() { _fv = WiFi.firmwareVersion(); - if (_fv < NINAFWVER) - return false; - return true; + 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 String& currentVersion, const String& requiredVersion) { + int curMajor, curMinor, curPatch; + int reqMajor, reqMinor, reqPatch; + int per_major, per_minor, per_patch; + + sscanf(currentVersion.c_str(), "%d.%d.%d", &curMajor, &curMinor, &curPatch); + sscanf(requiredVersion.c_str(), "%d.%d.%d", &reqMajor, &reqMinor, &reqPatch); + + if (curMajor != reqMajor) return curMajor > reqMajor; + if (curMinor != reqMinor) return curMinor > reqMinor; + return curPatch >= reqPatch; } /********************************************************/ @@ -261,39 +288,67 @@ 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 { - - // validate co-processor is physically connected connection - if (WiFi.status() == WL_NO_MODULE) { - WS_DEBUG_PRINT("No ESP32 module detected!"); - return; - } - - // validate co-processor's firmware version - if (!firmwareCheck()) - WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the " - "latest version."); - // disconnect from possible previous connection _disconnect(); - + feedWDT(); + WS_DEBUG_PRINT("Reset Pin: "); + WS_DEBUG_PRINTLN(_rstPin); // reset the esp32 if possible if (_rstPin != -1) { - WS_DEBUG("Resetting ESP32..."); + 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(10); + delay(50); digitalWrite(_rstPin, HIGH); delay(10); + if (_gpio0Pin != -1) { + pinMode(_gpio0Pin, INPUT); + } + // wait for the ESP32 to boot + delay(1000); } - // wait for the ESP32 to boot - delay(1000); + feedWDT(); + // WS_DEBUG_PRINT("ESP32 booted, version: "); + // WS_PRINTER.flush(); + // WS_DEBUG_PRINTLN(WiFi.firmwareVersion()); + // WS_PRINTER.flush(); + // feedWDT(); + + // // validate co-processor is physically connected connection + // if (WiFi.status() == WL_NO_MODULE) { + // WS_DEBUG_PRINT("No ESP32 module detected!"); + // WS_DEBUG_PRINT("Current Module Status:"); + // WS_DEBUG_PRINTLN(WiFi.status()); + // return; + // } + + // validate co-processor's firmware version + if (!firmwareCheck()) + WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the " + "latest version."); WS_DEBUG_PRINT("Connecting to "); WS_DEBUG_PRINTLN(_ssid); + WS_PRINTER.flush(); + feedWDT(); WiFi.begin(_ssid, _pass); _status = WS_NET_DISCONNECTED; + feedWDT(); + delay(5000); + feedWDT(); + delay(5000); + feedWDT(); } } diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h index 7cbcb1b03..d85c05b4f 100644 --- a/src/network_interfaces/Wippersnapper_WIFININA.h +++ b/src/network_interfaces/Wippersnapper_WIFININA.h @@ -280,21 +280,28 @@ class Wippersnapper_WIFININA : public Wippersnapper { #if defined(NINA_RESET_PIN) // reset the esp32 if possible, better if we didn't do this first time if (NINA_RESET_PIN != -1) { - WS_DEBUG("Resetting ESP32..."); + WS_DEBUG_PRINTLN("Resetting ESP32..."); + WS_PRINTER.flush(); pinMode(NINA_RESET_PIN, OUTPUT); digitalWrite(NINA_RESET_PIN, LOW); - delay(10); + delay(50); digitalWrite(NINA_RESET_PIN, HIGH); delay(10); } // wait for the ESP32 to boot - delay(1000); + delay(2000); + feedWDT(); #endif WS_DEBUG_PRINT("Connecting to "); WS_DEBUG_PRINTLN(_ssid); WiFi.begin(_ssid, _pass); _status = WS_NET_DISCONNECTED; + feedWDT(); + delay(5000); + feedWDT(); + delay(5000); + feedWDT(); } } diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index 5fac941f7..a9c483277 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -294,6 +294,9 @@ class ws_networking_pico : public Wippersnapper { } } _status = WS_NET_DISCONNECTED; + WS.feedWDT(); + delay(5000); + WS.feedWDT(); } } From 36edeef5f296d7691128ec05d1ec6ac24e643eb1 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 30 May 2024 18:16:21 +0100 Subject: [PATCH 20/54] WIP: end wifi object before restart --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 477b78dd2..eb14302e8 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -292,6 +292,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { } else { // disconnect from possible previous connection _disconnect(); + WiFi.end(); feedWDT(); WS_DEBUG_PRINT("Reset Pin: "); WS_DEBUG_PRINTLN(_rstPin); @@ -301,8 +302,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { WS_PRINTER.flush(); // Chip select for esp32 pinMode(_ssPin, OUTPUT); - digitalWrite(_ssPin, HIGH); - // Do we need to set SS low again? + digitalWrite(_ssPin, HIGH); // Do we need to set SS low again? if (_gpio0Pin != -1) { pinMode(_gpio0Pin, OUTPUT); digitalWrite(_gpio0Pin, LOW); @@ -316,7 +316,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { pinMode(_gpio0Pin, INPUT); } // wait for the ESP32 to boot - delay(1000); + delay(2000); } feedWDT(); // WS_DEBUG_PRINT("ESP32 booted, version: "); From 291d50ff0e59e7e2ed883cdc375d9823c0d40874 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 30 May 2024 18:50:14 +0100 Subject: [PATCH 21/54] WIP: restart SPI after wifi end/deinit --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index eb14302e8..9a4bef371 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -292,7 +292,10 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { } else { // disconnect from possible previous connection _disconnect(); + delay(100); WiFi.end(); + delay(100); + _wifi->begin(); feedWDT(); WS_DEBUG_PRINT("Reset Pin: "); WS_DEBUG_PRINTLN(_rstPin); @@ -319,11 +322,11 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { delay(2000); } feedWDT(); - // WS_DEBUG_PRINT("ESP32 booted, version: "); - // WS_PRINTER.flush(); - // WS_DEBUG_PRINTLN(WiFi.firmwareVersion()); - // WS_PRINTER.flush(); - // feedWDT(); + WS_DEBUG_PRINT("ESP32 booted, version: "); + WS_PRINTER.flush(); + WS_DEBUG_PRINTLN(WiFi.firmwareVersion()); + WS_PRINTER.flush(); + feedWDT(); // // validate co-processor is physically connected connection // if (WiFi.status() == WL_NO_MODULE) { From 3ad9bdca111e3a975aed518285be548533890676 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 30 May 2024 19:29:57 +0100 Subject: [PATCH 22/54] WIP: end and begin SPIWIFI --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 9a4bef371..9bdeab778 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -294,6 +294,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { _disconnect(); delay(100); WiFi.end(); + _wifi->end(); delay(100); _wifi->begin(); feedWDT(); From 4ec8fddaf0e12c6c79b55e89f64b358c709ff160 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 30 May 2024 20:50:45 +0100 Subject: [PATCH 23/54] Fix update RSSI call, triggering base class too --- src/Wippersnapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index e191f2f3d..83b55947f 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -2566,7 +2566,7 @@ void Wippersnapper::pingBroker() { WS_DEBUG_PRINTLN("FAILURE!"); } _prv_ping = millis(); - WS.getRSSI(); + getRSSI(); // update RSSI WS_DEBUG_PRINT("WiFi RSSI: "); WS_DEBUG_PRINTLN(WS._RSSI); } From baa0bdbb29c4058f68a94b183e8a2a53a7fcff26 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 30 May 2024 20:51:54 +0100 Subject: [PATCH 24/54] clang format --- .../Wippersnapper_AIRLIFT.h | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 9bdeab778..1f7ec8a5d 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -47,14 +47,14 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { */ /**************************************************************************/ Wippersnapper_AIRLIFT() : Wippersnapper() { - _ssPin = SPIWIFI_SS; // 10; - _ackPin = SPIWIFI_ACK; //7; + _ssPin = SPIWIFI_SS; // 10; + _ackPin = SPIWIFI_ACK; // 7; _rstPin = SPIWIFI_RESET; // 5; // should be 7 on PyPortals - #ifdef ESP32_GPIO0 +#ifdef ESP32_GPIO0 _gpio0Pin = ESP32_GPIO0; - #else +#else _gpio0Pin = -1; - #endif +#endif _wifi = &SPIWIFI; _ssid = 0; _pass = 0; @@ -195,17 +195,21 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { equal to the required version, False otherwise. */ /********************************************************/ - bool compareVersions(const String& currentVersion, const String& requiredVersion) { - int curMajor, curMinor, curPatch; - int reqMajor, reqMinor, reqPatch; - int per_major, per_minor, per_patch; - - sscanf(currentVersion.c_str(), "%d.%d.%d", &curMajor, &curMinor, &curPatch); - sscanf(requiredVersion.c_str(), "%d.%d.%d", &reqMajor, &reqMinor, &reqPatch); - - if (curMajor != reqMajor) return curMajor > reqMajor; - if (curMinor != reqMinor) return curMinor > reqMinor; - return curPatch >= reqPatch; + bool compareVersions(const String ¤tVersion, + const String &requiredVersion) { + int curMajor, curMinor, curPatch; + int reqMajor, reqMinor, reqPatch; + int per_major, per_minor, per_patch; + + sscanf(currentVersion.c_str(), "%d.%d.%d", &curMajor, &curMinor, &curPatch); + sscanf(requiredVersion.c_str(), "%d.%d.%d", &reqMajor, &reqMinor, + &reqPatch); + + if (curMajor != reqMajor) + return curMajor > reqMajor; + if (curMinor != reqMinor) + return curMinor > reqMinor; + return curPatch >= reqPatch; } /********************************************************/ @@ -288,7 +292,8 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { /**************************************************************************/ void _connect() { if (strlen(_ssid) == 0) { - _status = WS_SSID_INVALID; // possibly unneccesary as already checking elsewhere + _status = WS_SSID_INVALID; // possibly unneccesary as already checking + // elsewhere } else { // disconnect from possible previous connection _disconnect(); From 532e4887e0355cb6d7b4e782123d3c07a6b839cc Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 30 May 2024 23:55:35 +0100 Subject: [PATCH 25/54] Fix: runNetFSM if ping fails --- src/Wippersnapper.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 83b55947f..e25b60ebd 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -2563,7 +2563,9 @@ void Wippersnapper::pingBroker() { if (WS._mqtt->ping()) { WS_DEBUG_PRINTLN("SUCCESS!"); } else { - WS_DEBUG_PRINTLN("FAILURE!"); + WS_DEBUG_PRINTLN("FAILURE! Running network FSM..."); + WS._mqtt->disconnect(); + runNetFSM(); } _prv_ping = millis(); getRSSI(); // update RSSI From 5ef4b2a25949947d16246e603dd5d9309a45de16 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 09:22:36 +0100 Subject: [PATCH 26/54] Relabel PING to MQTT PING --- src/Wippersnapper.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index e25b60ebd..67ad018c7 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -2558,8 +2558,7 @@ 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_PRINT("Sending PING: "); - // TODO: Add back, is crashing currently + WS_DEBUG_PRINT("Sending MQTT PING: "); if (WS._mqtt->ping()) { WS_DEBUG_PRINTLN("SUCCESS!"); } else { From 72e746c788a40a583ab252df53802f4b6fb73951 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 09:37:09 +0100 Subject: [PATCH 27/54] Remove unnecessary delay after setPins + extra vars --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 1f7ec8a5d..db57ac569 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -62,7 +62,6 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { // setup ESP32 co-processor pins during init. WiFi.setPins(_ssPin, _ackPin, _rstPin, _gpio0Pin, _wifi); - delay(1000); } /**************************************************************************/ @@ -199,7 +198,6 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { const String &requiredVersion) { int curMajor, curMinor, curPatch; int reqMajor, reqMinor, reqPatch; - int per_major, per_minor, per_patch; sscanf(currentVersion.c_str(), "%d.%d.%d", &curMajor, &curMinor, &curPatch); sscanf(requiredVersion.c_str(), "%d.%d.%d", &reqMajor, &reqMinor, From 4686f23519c452610828ad8bb049d663c6d8895a Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 09:48:24 +0100 Subject: [PATCH 28/54] Add reset method for ESP32 Airlift --- .../Wippersnapper_AIRLIFT.h | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index db57ac569..149cf930a 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -304,27 +304,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { WS_DEBUG_PRINT("Reset Pin: "); WS_DEBUG_PRINTLN(_rstPin); // reset the esp32 if possible - 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); - } + resetESP32(); feedWDT(); WS_DEBUG_PRINT("ESP32 booted, version: "); WS_PRINTER.flush(); @@ -359,6 +339,35 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { } } + /**************************************************************************/ + /*! + @brief Resets the ESP32 module. + */ + /**************************************************************************/ + void resetESP32(){ + 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); + } + } + /**************************************************************************/ /*! @brief Disconnects from the wireless network. From c657cc6693847e18099f2641bfdb60f6282d41e3 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 10:46:57 +0100 Subject: [PATCH 29/54] Cleanup comments --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 149cf930a..9ce44b241 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -304,7 +304,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { WS_DEBUG_PRINT("Reset Pin: "); WS_DEBUG_PRINTLN(_rstPin); // reset the esp32 if possible - resetESP32(); + resetAirLift(); feedWDT(); WS_DEBUG_PRINT("ESP32 booted, version: "); WS_PRINTER.flush(); @@ -312,14 +312,6 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { WS_PRINTER.flush(); feedWDT(); - // // validate co-processor is physically connected connection - // if (WiFi.status() == WL_NO_MODULE) { - // WS_DEBUG_PRINT("No ESP32 module detected!"); - // WS_DEBUG_PRINT("Current Module Status:"); - // WS_DEBUG_PRINTLN(WiFi.status()); - // return; - // } - // validate co-processor's firmware version if (!firmwareCheck()) WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the " @@ -344,7 +336,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { @brief Resets the ESP32 module. */ /**************************************************************************/ - void resetESP32(){ + void resetAirLift(){ if (_rstPin != -1) { WS_DEBUG_PRINTLN("Resetting ESP32..."); WS_PRINTER.flush(); From 4b3cdfabceaaa2c0cc3a29f0fb3c4e7df1b1545d Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 11:36:33 +0100 Subject: [PATCH 30/54] Formatting --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 9ce44b241..2101da9a0 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -313,9 +313,10 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { 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."); + } WS_DEBUG_PRINT("Connecting to "); WS_DEBUG_PRINTLN(_ssid); @@ -336,7 +337,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { @brief Resets the ESP32 module. */ /**************************************************************************/ - void resetAirLift(){ + void resetAirLift() { if (_rstPin != -1) { WS_DEBUG_PRINTLN("Resetting ESP32..."); WS_PRINTER.flush(); From 80b3cdfcac8f7fb8e542e54eb62decd6fd6c2813 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 11:48:38 +0100 Subject: [PATCH 31/54] Add WS_DELAY and RETRY_FUNCTION_UNTIL_TIMEOUT --- src/Wippersnapper.h | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index c0ca6b78b..ce59d959c 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -59,6 +59,56 @@ {} ///< 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 From 9ee84b38d57a824e7f4142cdb61f8ee05ec3ce09 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 12:57:10 +0100 Subject: [PATCH 32/54] Swap AIRLIFT delays after wifi attempt to actually check status --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 2101da9a0..1b1b6ee63 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -324,11 +324,19 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { feedWDT(); WiFi.begin(_ssid, _pass); _status = WS_NET_DISCONNECTED; - feedWDT(); - delay(5000); - feedWDT(); - delay(5000); - feedWDT(); + + // Use the macro to retry the status check until connected / timed out + int lastResult; + RETRY_FUNCTION_UNTIL_TIMEOUT( + []() -> int { /* no-op */ }, // Function to call each cycle + int, // return type + lastResult, // return variable (unused here) + [](int status) { return WiFi.status() == WL_CONNECTED; }, // check + 15000, // timeout interval (ms) + 200); // interval between retries + + // wait 2seconds for connection to stabilize + WS_DELAY_WITH_WDT(2000); } } From 1f7818419f902308c60787b499ed27dea71cece0 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 13:03:02 +0100 Subject: [PATCH 33/54] Revert WIFININA changes (reset code), leaving RSSI logging --- .../Wippersnapper_WIFININA.h | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h index d85c05b4f..ea301cf4f 100644 --- a/src/network_interfaces/Wippersnapper_WIFININA.h +++ b/src/network_interfaces/Wippersnapper_WIFININA.h @@ -268,40 +268,8 @@ class Wippersnapper_WIFININA : public Wippersnapper { // disconnect from possible previous connection _disconnect(); -// no clear recommendation, all three defined for both boards, future-proofing -#if defined(NINA_RESETN) -#define NINA_RESET_PIN NINA_RESETN -#elif defined(SPIWIFI_RESET) -#define NINA_RESET_PIN SPIWIFI_RESET -#elif defined(ESP32_RESETN) -#define NINA_RESET_PIN ESP32_RESETN -#endif - -#if defined(NINA_RESET_PIN) - // reset the esp32 if possible, better if we didn't do this first time - if (NINA_RESET_PIN != -1) { - WS_DEBUG_PRINTLN("Resetting ESP32..."); - WS_PRINTER.flush(); - pinMode(NINA_RESET_PIN, OUTPUT); - digitalWrite(NINA_RESET_PIN, LOW); - delay(50); - digitalWrite(NINA_RESET_PIN, HIGH); - delay(10); - } - // wait for the ESP32 to boot - delay(2000); - feedWDT(); -#endif - - WS_DEBUG_PRINT("Connecting to "); - WS_DEBUG_PRINTLN(_ssid); WiFi.begin(_ssid, _pass); _status = WS_NET_DISCONNECTED; - feedWDT(); - delay(5000); - feedWDT(); - delay(5000); - feedWDT(); } } From 0cd45cf3fae3f1eeedc3f132b0279d235dbc87b4 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 13:38:26 +0100 Subject: [PATCH 34/54] Swap Pico and AirLift to check status after wifi begin with macro --- .../Wippersnapper_AIRLIFT.h | 19 +++++++----- src/network_interfaces/ws_networking_pico.h | 31 ++++++++++--------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 1b1b6ee63..76b43f10a 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -328,15 +328,20 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { // Use the macro to retry the status check until connected / timed out int lastResult; RETRY_FUNCTION_UNTIL_TIMEOUT( - []() -> int { /* no-op */ }, // Function to call each cycle - int, // return type - lastResult, // return variable (unused here) - [](int status) { return WiFi.status() == WL_CONNECTED; }, // check - 15000, // timeout interval (ms) + []() -> int { return WiFi.status(); }, // Function call each cycle + int, // return type + lastResult, // return variable (unused here) + [](int status) { status == WL_CONNECTED; }, // check + 20000, // timeout interval (ms) 200); // interval between retries - // wait 2seconds for connection to stabilize - WS_DELAY_WITH_WDT(2000); + 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? + } } } diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index a9c483277..e19d83c87 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -282,21 +282,24 @@ class ws_networking_pico : public Wippersnapper { WiFi.setTimeout(20000); WS.feedWDT(); 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) { status == WL_CONNECTED; }, // check + 20000, // timeout interval (ms) + 200); // 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; } - _status = WS_NET_DISCONNECTED; - WS.feedWDT(); - delay(5000); - WS.feedWDT(); } } From 2f11a9e1bf8287d9c80f3d601122384bb1550cf2 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 13:39:09 +0100 Subject: [PATCH 35/54] Clang format --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 8 ++++---- src/network_interfaces/ws_networking_pico.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 76b43f10a..0fd8bd83a 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -329,11 +329,11 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { int lastResult; RETRY_FUNCTION_UNTIL_TIMEOUT( []() -> int { return WiFi.status(); }, // Function call each cycle - int, // return type - lastResult, // return variable (unused here) + int, // return type + lastResult, // return variable (unused here) [](int status) { status == WL_CONNECTED; }, // check - 20000, // timeout interval (ms) - 200); // interval between retries + 20000, // timeout interval (ms) + 200); // interval between retries if (lastResult == WL_CONNECTED) { _status = WS_NET_CONNECTED; diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index e19d83c87..7f1a4ae24 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -287,11 +287,11 @@ class ws_networking_pico : public Wippersnapper { int lastResult; RETRY_FUNCTION_UNTIL_TIMEOUT( []() -> int { return WiFi.status(); }, // Function call each cycle - int, // return type - lastResult, // return variable (unused here) + int, // return type + lastResult, // return variable (unused here) [](int status) { status == WL_CONNECTED; }, // check - 20000, // timeout interval (ms) - 200); // interval between retries + 20000, // timeout interval (ms) + 200); // interval between retries if (lastResult == WL_CONNECTED) { _status = WS_NET_CONNECTED; From c4f3d0a38b062d9e0b967ed098eaa99fcdc7cce1 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 13:56:27 +0100 Subject: [PATCH 36/54] Correct return types for lambda conditionals --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 6 +++--- src/network_interfaces/ws_networking_pico.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 0fd8bd83a..544ab80d3 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -331,9 +331,9 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { []() -> int { return WiFi.status(); }, // Function call each cycle int, // return type lastResult, // return variable (unused here) - [](int status) { status == WL_CONNECTED; }, // check - 20000, // timeout interval (ms) - 200); // interval between retries + [](int status) { return status == WL_CONNECTED; }, // check + 20000, // timeout interval (ms) + 200); // interval between retries if (lastResult == WL_CONNECTED) { _status = WS_NET_CONNECTED; diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index 7f1a4ae24..13c23581a 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -289,9 +289,9 @@ class ws_networking_pico : public Wippersnapper { []() -> int { return WiFi.status(); }, // Function call each cycle int, // return type lastResult, // return variable (unused here) - [](int status) { status == WL_CONNECTED; }, // check - 20000, // timeout interval (ms) - 200); // interval between retries + [](int status) { return status == WL_CONNECTED; }, // check + 20000, // timeout interval (ms) + 200); // interval between retries if (lastResult == WL_CONNECTED) { _status = WS_NET_CONNECTED; From e16c2c427b733f3e5edd16ef56a55cb1764b09ba Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 14:54:40 +0100 Subject: [PATCH 37/54] clang format wippersnapper.h --- src/Wippersnapper.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index ce59d959c..36e4bd060 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -72,7 +72,6 @@ } \ } ///< Delay function - /**************************************************************************/ /*! @brief Retry a function until a condition is met or a timeout is reached. @@ -108,7 +107,6 @@ } \ } ///< Retry a function until a condition is met or a timeout is reached. - // Wippersnapper pb helpers #include From 97f6407ef658f41c6e4277408b926159e20baefb Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 14 Jun 2024 15:43:51 +0100 Subject: [PATCH 38/54] Swap RSSI to an accessor method instead of saving + format --- src/Wippersnapper.cpp | 9 +++++---- src/Wippersnapper.h | 3 +-- src/network_interfaces/Wippersnapper_AIRLIFT.h | 7 ++----- src/network_interfaces/Wippersnapper_ESP32.h | 7 ++----- src/network_interfaces/Wippersnapper_ESP8266.h | 7 ++----- src/network_interfaces/Wippersnapper_WIFININA.h | 7 ++----- src/network_interfaces/ws_networking_pico.h | 7 ++----- 7 files changed, 16 insertions(+), 31 deletions(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 67ad018c7..731f8b7b6 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -185,10 +185,12 @@ void Wippersnapper::getMacAddr() { /****************************************************************************/ /*! @brief Gets the network's RSSI. + @return int32_t RSSI value */ /****************************************************************************/ -void Wippersnapper::getRSSI() { +int32_t Wippersnapper::getRSSI() { WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); + return 0; } /****************************************************************************/ @@ -2401,7 +2403,7 @@ void Wippersnapper::runNetFSM() { WS_LED_STATUS_WIFI_CONNECTING); } WS_DEBUG_PRINT("SSID found! RSSI: "); - WS_DEBUG_PRINTLN(WS._RSSI); + WS_DEBUG_PRINTLN(WS.getRSSI()); // Attempt to connect to wireless network maxAttempts = 5; while (maxAttempts > 0) { @@ -2567,9 +2569,8 @@ void Wippersnapper::pingBroker() { runNetFSM(); } _prv_ping = millis(); - getRSSI(); // update RSSI WS_DEBUG_PRINT("WiFi RSSI: "); - WS_DEBUG_PRINTLN(WS._RSSI); + WS_DEBUG_PRINTLN(WS.getRSSI()); } // blink status LED every STATUS_LED_KAT_BLINK_TIME millis if (millis() > (_prvKATBlink + STATUS_LED_KAT_BLINK_TIME)) { diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index 36e4bd060..f1bbe227d 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -270,7 +270,7 @@ class Wippersnapper { void disconnect(); virtual void getMacAddr(); - virtual void getRSSI(); + virtual int32_t getRSSI(); virtual void setupMQTTClient(const char *clientID); virtual ws_status_t networkStatus(); @@ -362,7 +362,6 @@ class Wippersnapper { ws_uart *_uartComponent; ///< Instance of UART class // TODO: does this really need to be global? - int32_t _RSSI = 0; ///< RSSI value of the network connection uint8_t _macAddr[6]; /*!< Unique network iface identifier */ char sUID[13]; /*!< Unique network iface identifier */ const char *_boardId; /*!< Adafruit IO+ board string */ diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 544ab80d3..b3beb2c9c 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -120,7 +120,6 @@ 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) { - WS._RSSI = WiFi.RSSI(i); return true; } } @@ -225,12 +224,10 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { /********************************************************/ /*! @brief Gets the current network RSSI value + @return int32_t RSSI value */ /********************************************************/ - void getRSSI() { - // test if this fails when disconnected or returns something sensible - WS._RSSI = WiFi.RSSI(); - } + int32_t getRSSI() { return WiFi.RSSI(); } /********************************************************/ /*! diff --git a/src/network_interfaces/Wippersnapper_ESP32.h b/src/network_interfaces/Wippersnapper_ESP32.h index cf1b1c780..2f4f2b4cb 100644 --- a/src/network_interfaces/Wippersnapper_ESP32.h +++ b/src/network_interfaces/Wippersnapper_ESP32.h @@ -110,7 +110,6 @@ 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._RSSI = WiFi.RSSI(i); return true; } } @@ -143,12 +142,10 @@ class Wippersnapper_ESP32 : public Wippersnapper { /********************************************************/ /*! @brief Gets the current network RSSI value + @return int32_t RSSI value */ /********************************************************/ - void getRSSI() { - // test if this fails when disconnected or returns something sensible - WS._RSSI = WiFi.RSSI(); - } + int32_t getRSSI() { return WiFi.RSSI(); } /********************************************************/ /*! diff --git a/src/network_interfaces/Wippersnapper_ESP8266.h b/src/network_interfaces/Wippersnapper_ESP8266.h index 51f5df861..5e48a9b33 100644 --- a/src/network_interfaces/Wippersnapper_ESP8266.h +++ b/src/network_interfaces/Wippersnapper_ESP8266.h @@ -133,7 +133,6 @@ 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._RSSI = WiFi.RSSI(i); return true; } } @@ -166,12 +165,10 @@ class Wippersnapper_ESP8266 : public Wippersnapper { /********************************************************/ /*! @brief Gets the current network RSSI value + @return int32_t RSSI value */ /********************************************************/ - void getRSSI() { - // test if this fails when disconnected or returns something sensible - WS._RSSI = WiFi.RSSI(); - } + int32_t getRSSI() { return WiFi.RSSI(); } /*******************************************************************/ /*! diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h index ea301cf4f..33d43e8e4 100644 --- a/src/network_interfaces/Wippersnapper_WIFININA.h +++ b/src/network_interfaces/Wippersnapper_WIFININA.h @@ -131,7 +131,6 @@ 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) { - WS._RSSI = WiFi.RSSI(i); return true; } } @@ -190,12 +189,10 @@ class Wippersnapper_WIFININA : public Wippersnapper { /********************************************************/ /*! @brief Gets the current network RSSI value + @return int32_t RSSI value */ /********************************************************/ - void getRSSI() { - // test if this fails when disconnected or returns something sensible - WS._RSSI = WiFi.RSSI(); - } + int32_t getRSSI() { return WiFi.RSSI(); } /********************************************************/ /*! diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index 13c23581a..efd513051 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -109,7 +109,6 @@ 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._RSSI = WiFi.RSSI(i); return true; } } @@ -142,12 +141,10 @@ class ws_networking_pico : public Wippersnapper { /********************************************************/ /*! @brief Gets the current network RSSI value + @return int32_t RSSI value */ /********************************************************/ - void getRSSI() { - // test if this fails when disconnected or returns something sensible - WS._RSSI = WiFi.RSSI(); - } + int32_t getRSSI() { return WiFi.RSSI(); } /********************************************************/ /*! From 8c338bec398c245ffb51b48f4a4de1a875b9022a Mon Sep 17 00:00:00 2001 From: tyeth Date: Mon, 1 Jul 2024 14:26:50 +0100 Subject: [PATCH 39/54] Remove debug print of Airlift reset pin value --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index b3beb2c9c..de0c53680 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -298,11 +298,10 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { delay(100); _wifi->begin(); feedWDT(); - WS_DEBUG_PRINT("Reset Pin: "); - WS_DEBUG_PRINTLN(_rstPin); // reset the esp32 if possible resetAirLift(); feedWDT(); + WS_DEBUG_PRINT("ESP32 booted, version: "); WS_PRINTER.flush(); WS_DEBUG_PRINTLN(WiFi.firmwareVersion()); From ea35d731175004bf7f7e501c10144c9ce8520e1b Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Mon, 1 Jul 2024 18:13:21 +0100 Subject: [PATCH 40/54] Correct ws_pb_helpers includes + CompareVersion for Airlift FW + RSSI label --- src/Wippersnapper.cpp | 2 +- src/nanopb/ws_pb_helpers.cpp | 13 ++++----- src/nanopb/ws_pb_helpers.h | 2 ++ .../Wippersnapper_AIRLIFT.h | 29 ++++++++++++------- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 731f8b7b6..7453ece39 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -185,7 +185,7 @@ void Wippersnapper::getMacAddr() { /****************************************************************************/ /*! @brief Gets the network's RSSI. - @return int32_t RSSI value + @return int32_t RSSI value, 0 to 255, in dB */ /****************************************************************************/ int32_t Wippersnapper::getRSSI() { diff --git a/src/nanopb/ws_pb_helpers.cpp b/src/nanopb/ws_pb_helpers.cpp index a8e49d9c8..a3b2b8ef1 100644 --- a/src/nanopb/ws_pb_helpers.cpp +++ b/src/nanopb/ws_pb_helpers.cpp @@ -1,5 +1,5 @@ /*! - * @file ws_pb_helpers.h + * @file ws_pb_helpers.cpp * * Protobuf encode/decode helpers with error logging for Wippersnapper. * @@ -12,12 +12,9 @@ * BSD license, all text here must be included in any redistribution. * */ -#include "pb.h" -#include "pb_decode.h" -#include "pb_encode.h" -#include "ws_pb_helpers.h" -#include +#include "ws_pb_helpers.h" +#include "../Wippersnapper.h" // ***************************************************************************** /*! @@ -32,7 +29,7 @@ !*/ // ***************************************************************************** bool ws_pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, - void *dest_struct) { + void *dest_struct) { bool status = pb_decode(stream, fields, dest_struct); if (!status) { WS_DEBUG_PRINT("Protobuf decode error: "); @@ -54,7 +51,7 @@ bool ws_pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, !*/ // ***************************************************************************** bool ws_pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, - const void *src_struct) { + const void *src_struct) { bool status = pb_encode(stream, fields, src_struct); if (!status) { WS_DEBUG_PRINT("Protobuf encode error: "); diff --git a/src/nanopb/ws_pb_helpers.h b/src/nanopb/ws_pb_helpers.h index 67ef98467..78a0d5c10 100644 --- a/src/nanopb/ws_pb_helpers.h +++ b/src/nanopb/ws_pb_helpers.h @@ -16,6 +16,8 @@ #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); diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index de0c53680..e51829bb1 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -193,14 +193,23 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { equal to the required version, False otherwise. */ /********************************************************/ - bool compareVersions(const String ¤tVersion, - const String &requiredVersion) { - int curMajor, curMinor, curPatch; - int reqMajor, reqMinor, reqPatch; - - sscanf(currentVersion.c_str(), "%d.%d.%d", &curMajor, &curMinor, &curPatch); - sscanf(requiredVersion.c_str(), "%d.%d.%d", &reqMajor, &reqMinor, - &reqPatch); + bool compareVersions(const char *currentVersion, + const char *requiredVersion) { + int curMajor, curMinor, curPatch = 0; + int reqMajor, reqMinor, 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; + } if (curMajor != reqMajor) return curMajor > reqMajor; @@ -272,7 +281,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; /*!< nina-fw firmware version. */ int _ssPin = -1; /*!< SPI S.S. pin. */ int _ackPin = -1; /*!< SPI ACK pin. */ int _rstPin = -1; /*!< SPI RST pin. */ @@ -326,7 +335,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { RETRY_FUNCTION_UNTIL_TIMEOUT( []() -> int { return WiFi.status(); }, // Function call each cycle int, // return type - lastResult, // return variable (unused here) + lastResult, // return variable [](int status) { return status == WL_CONNECTED; }, // check 20000, // timeout interval (ms) 200); // interval between retries From 75b9aff477ba774a0c907217bbce70583e27b85d Mon Sep 17 00:00:00 2001 From: tyeth Date: Tue, 2 Jul 2024 14:58:52 +0100 Subject: [PATCH 41/54] Swap line endings to avoid showing in diff + no secrets --- .gitignore | 99 +- src/Wippersnapper.h | 988 +++++++++--------- .../analogIO/Wippersnapper_AnalogIO.cpp | 790 +++++++------- src/components/ds18x20/ws_ds18x20.cpp | 624 +++++------ src/components/pixels/ws_pixels.cpp | 904 ++++++++-------- .../uart/drivers/ws_uart_drv_pm25aqi.h | 428 ++++---- .../Wippersnapper_WIFININA.h | 566 +++++----- 7 files changed, 2201 insertions(+), 2198 deletions(-) diff --git a/.gitignore b/.gitignore index 4ab1f2a5d..b4c22ca55 100644 --- a/.gitignore +++ b/.gitignore @@ -1,48 +1,51 @@ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - -# Doxygen -*.bak -html/* - - -# VSCode artifacts -.vscode/* -src/.vscode/settings.json - -.DS_STORE - -examples/Wippersnapper_demo/build/ - -#Platformio artifacts -.pio/ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Doxygen +*.bak +html/* + + +# VSCode artifacts +.vscode/* +src/.vscode/settings.json + +.DS_STORE + +examples/Wippersnapper_demo/build/ + +# Platformio artifacts +.pio/ + +# Secrets +data/ \ No newline at end of file diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index f1bbe227d..c202db569 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -1,494 +1,494 @@ -/*! - * @file Wippersnapper.h - * - * This is the documentation for Adafruit's Wippersnapper firmware for the - * Arduino platform. It is designed specifically to work with - * Adafruit IO Wippersnapper IoT platform. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2020-2024 for Adafruit Industries. - * - * BSD license, all text here must be included in any redistribution. - * - */ - -#ifndef WIPPERSNAPPER_H -#define WIPPERSNAPPER_H - -// Cpp STD -#include - -// Nanopb dependencies -#include -#include -#include -#include - -#include // description.proto -#include // signal.proto - -// External libraries -#include "Adafruit_MQTT.h" // MQTT Client -#include "Adafruit_SleepyDog.h" // Watchdog -#include "Arduino.h" // Wiring -#include // SPI - -// Wippersnapper API Helpers -#include "Wippersnapper_Boards.h" -#include "components/statusLED/Wippersnapper_StatusLED.h" -#include "provisioning/ConfigJson.h" - -#define WS_DEBUG ///< Define to enable debugging to serial terminal -#define WS_PRINTER Serial ///< Where debug messages will be printed - -// Define actual debug output functions when necessary. -#ifdef WS_DEBUG -#define WS_DEBUG_PRINT(...) \ - { WS_PRINTER.print(__VA_ARGS__); } ///< Prints debug output. -#define WS_DEBUG_PRINTLN(...) \ - { WS_PRINTER.println(__VA_ARGS__); } ///< Prints line from debug output. -#define WS_DEBUG_PRINTHEX(...) \ - { WS_PRINTER.print(__VA_ARGS__, HEX); } ///< Prints debug output. -#else -#define WS_DEBUG_PRINT(...) \ - {} ///< Prints debug output -#define WS_DEBUG_PRINTLN(...) \ - {} ///< 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" -#include "components/i2c/WipperSnapper_I2C.h" - -// LEDC-Manager, ESP32-only -#ifdef ARDUINO_ARCH_ESP32 -#include "components/ledc/ws_ledc.h" -#endif - -// Display -#ifdef USE_DISPLAY -#include "display/ws_display_driver.h" -#include "display/ws_display_ui_helper.h" -#endif - -#include "components/ds18x20/ws_ds18x20.h" -#include "components/pixels/ws_pixels.h" -#include "components/pwm/ws_pwm.h" -#include "components/servo/ws_servo.h" -#include "components/uart/ws_uart.h" - -#if defined(USE_TINYUSB) -#include "provisioning/tinyusb/Wippersnapper_FS.h" -#endif - -#if defined(USE_LITTLEFS) -#include "provisioning/littlefs/WipperSnapper_LittleFS.h" -#endif - -#define WS_VERSION \ - "1.0.0-beta.85" ///< WipperSnapper app. version (semver-formatted) - -// Reserved Adafruit IO MQTT topics -#define TOPIC_IO_THROTTLE "/throttle" ///< Adafruit IO Throttle MQTT Topic -#define TOPIC_IO_ERRORS "/errors" ///< Adafruit IO Error MQTT Topic - -// Reserved Wippersnapper topics -#define TOPIC_WS "/wprsnpr/" ///< WipperSnapper topic -#define TOPIC_INFO "/info/" ///< Registration sub-topic -#define TOPIC_SIGNALS "/signals/" ///< Signals sub-topic -#define TOPIC_I2C "/i2c" ///< I2C sub-topic -#define MQTT_TOPIC_PIXELS_DEVICE \ - "/signals/device/pixel" ///< Pixels device->broker topic -#define MQTT_TOPIC_PIXELS_BROKER \ - "/signals/broker/pixel" ///< Pixels broker->device topic - -/** Defines the Adafruit IO connection status */ -typedef enum { - WS_IDLE = 0, // Waiting for connection establishement - WS_NET_DISCONNECTED = 1, // Network disconnected - WS_DISCONNECTED = 2, // Disconnected from Adafruit IO - WS_FINGERPRINT_UNKOWN = 3, // Unknown WS_SSL_FINGERPRINT - - WS_NET_CONNECT_FAILED = 10, // Failed to connect to network - WS_CONNECT_FAILED = 11, // Failed to connect to Adafruit IO - WS_FINGERPRINT_INVALID = 12, // Unknown WS_SSL_FINGERPRINT - WS_AUTH_FAILED = 13, // Invalid Adafruit IO login credentials provided. - WS_SSID_INVALID = - 14, // SSID is "" or otherwise invalid, connection not attempted - - WS_NET_CONNECTED = 20, // Connected to Adafruit IO - WS_CONNECTED = 21, // Connected to network - WS_CONNECTED_INSECURE = 22, // Insecurely (non-SSL) connected to network - WS_FINGERPRINT_UNSUPPORTED = 23, // Unsupported WS_SSL_FINGERPRINT - WS_FINGERPRINT_VALID = 24, // Valid WS_SSL_FINGERPRINT - WS_BOARD_DESC_INVALID = 25, // Unable to send board description - WS_BOARD_RESYNC_FAILED = 26 // Board sync failure -} ws_status_t; - -/** Defines the Adafruit IO MQTT broker's connection return codes */ -typedef enum { - WS_MQTT_CONNECTED = 0, // Connected - WS_MQTT_INVALID_PROTOCOL = 1, // Invalid mqtt protocol - WS_MQTT_INVALID_CID = 2, // Client id rejected - WS_MQTT_SERVICE_UNAVALIABLE = 3, // Malformed user/pass - WS_MQTT_INVALID_USER_PASS = 4, // Unauthorized access to resource - WS_MQTT_UNAUTHORIZED = 5, // MQTT service unavailable - WS_MQTT_THROTTLED = 6, // Account throttled - WS_MQTT_BANNED = 7 // Account banned -} ws_mqtt_status_t; - -/** Defines the Wippersnapper client's hardware registration status */ -typedef enum { - WS_BOARD_DEF_IDLE, - WS_BOARD_DEF_SEND_FAILED, - WS_BOARD_DEF_SENT, - WS_BOARD_DEF_OK, - WS_BOARD_DEF_INVALID, - WS_BOARD_DEF_UNSPECIFIED -} ws_board_status_t; - -/** Defines the Wippersnapper client's network status */ -typedef enum { - FSM_NET_IDLE, - FSM_NET_CONNECTED, - FSM_MQTT_CONNECTED, - FSM_NET_CHECK_MQTT, - FSM_NET_CHECK_NETWORK, - FSM_NET_ESTABLISH_NETWORK, - FSM_NET_ESTABLISH_MQTT, -} fsm_net_t; - -#define WS_WDT_TIMEOUT 60000 ///< WDT timeout -/* MQTT Configuration */ -#define WS_KEEPALIVE_INTERVAL_MS \ - 5000 ///< Session keepalive interval time, in milliseconds - -#define WS_MQTT_MAX_PAYLOAD_SIZE \ - 512 ///< MAXIMUM expected payload size, in bytes - -class Wippersnapper_DigitalGPIO; -class Wippersnapper_AnalogIO; -class Wippersnapper_FS; -class WipperSnapper_LittleFS; -#ifdef USE_DISPLAY -class ws_display_driver; -class ws_display_ui_helper; -#endif -#ifdef ARDUINO_ARCH_ESP32 -class ws_ledc; -#endif -class WipperSnapper_Component_I2C; -class ws_servo; -class ws_pwm; -class ws_ds18x20; -class ws_pixels; -class ws_uart; - -/**************************************************************************/ -/*! - @brief Class that provides storage and functions for the Adafruit IO - Wippersnapper interface. -*/ -/**************************************************************************/ -class Wippersnapper { -public: - Wippersnapper(); - virtual ~Wippersnapper(); - - void provision(); - - bool lockStatusNeoPixel; ///< True if status LED is using the status neopixel - bool lockStatusDotStar; ///< True if status LED is using the status dotstar - bool lockStatusLED; ///< True if status LED is using the built-in LED - float status_pixel_brightness = - STATUS_PIXEL_BRIGHTNESS_DEFAULT; ///< Global status pixel's brightness - ///< (from 0.0 to 1.0) - - virtual void set_user_key(); - virtual void set_ssid_pass(const char *ssid, const char *ssidPassword); - virtual void set_ssid_pass(); - virtual bool check_valid_ssid(); - - virtual void _connect(); - virtual void _disconnect(); - void connect(); - void disconnect(); - - virtual void getMacAddr(); - virtual int32_t getRSSI(); - virtual void setupMQTTClient(const char *clientID); - - virtual ws_status_t networkStatus(); - ws_board_status_t getBoardStatus(); - - bool generateDeviceUID(); - bool generateWSTopics(); - bool generateWSErrorTopics(); - - // Registration API - bool registerBoard(); - bool encodePubRegistrationReq(); - void decodeRegistrationResp(char *data, uint16_t len); - void pollRegistrationResp(); - // Configuration API - void publishPinConfigComplete(); - - // run() loop - ws_status_t run(); - void processPackets(); - void publish(const char *topic, uint8_t *payload, uint16_t bLen, - uint8_t qos = 0); - - // Networking helpers - void pingBroker(); - void runNetFSM(); - - // WDT helpers - void enableWDT(int timeoutMS = 0); - void feedWDT(); - - // Error handling helpers - void haltError(String error, - ws_led_status_t ledStatusColor = WS_LED_STATUS_ERROR_RUNTIME); - void errorWriteHang(String error); - - // MQTT topic callbacks // - // Decodes a signal message - bool decodeSignalMsg( - wippersnapper_signal_v1_CreateSignalRequest *encodedSignalMsg); - - // Encodes a pin event message - bool - encodePinEvent(wippersnapper_signal_v1_CreateSignalRequest *outgoingSignalMsg, - uint8_t pinName, int pinVal); - - // Pin configure message - bool configureDigitalPinReq(wippersnapper_pin_v1_ConfigurePinRequest *pinMsg); - bool configAnalogInPinReq(wippersnapper_pin_v1_ConfigurePinRequest *pinMsg); - - // I2C - std::vector - i2cComponents; ///< Vector containing all I2C components - WipperSnapper_Component_I2C *_i2cPort0 = - NULL; ///< WipperSnapper I2C Component for I2C port #0 - WipperSnapper_Component_I2C *_i2cPort1 = - NULL; ///< WipperSnapper I2C Component for I2C port #1 - bool _isI2CPort0Init = - false; ///< True if I2C port 0 has been initialized, False otherwise. - bool _isI2CPort1Init = - false; ///< True if I2C port 1 has been initialized, False otherwise. - - uint8_t _buffer[WS_MQTT_MAX_PAYLOAD_SIZE]; /*!< Shared buffer to save callback - payload */ - uint8_t - _buffer_outgoing[WS_MQTT_MAX_PAYLOAD_SIZE]; /*!< buffer which contains - outgoing payload data */ - uint16_t bufSize; /*!< Length of data inside buffer */ - - ws_board_status_t _boardStatus = - WS_BOARD_DEF_IDLE; ///< Hardware's registration status - - // TODO: We really should look at making these static definitions, not dynamic - // to free up space on the heap - Wippersnapper_DigitalGPIO *_digitalGPIO; ///< Instance of digital gpio class - Wippersnapper_AnalogIO *_analogIO; ///< Instance of analog io class - Wippersnapper_FS *_fileSystem; ///< Instance of Filesystem (native USB) - WipperSnapper_LittleFS - *_littleFS; ///< Instance of LittleFS Filesystem (non-native USB) -#ifdef USE_DISPLAY - ws_display_driver *_display = nullptr; ///< Instance of display driver class - ws_display_ui_helper *_ui_helper = - nullptr; ///< Instance of display UI helper class -#endif - ws_pixels *_ws_pixelsComponent; ///< ptr to instance of ws_pixels class - ws_pwm *_pwmComponent; ///< Instance of pwm class - ws_servo *_servoComponent; ///< Instance of servo class - ws_ds18x20 *_ds18x20Component; ///< Instance of DS18x20 class - ws_uart *_uartComponent; ///< Instance of UART class - - // TODO: does this really need to be global? - uint8_t _macAddr[6]; /*!< Unique network iface identifier */ - char sUID[13]; /*!< Unique network iface identifier */ - const char *_boardId; /*!< Adafruit IO+ board string */ - Adafruit_MQTT *_mqtt; /*!< Reference to Adafruit_MQTT, _mqtt. */ - - secretsConfig _config; /*!< Wippersnapper secrets.json as a struct. */ - - // TODO: Does this need to be within this class? - int32_t totalDigitalPins; /*!< Total number of digital-input capable pins */ - - char *_topic_description = NULL; /*!< MQTT topic for the device description */ - char *_topic_signal_device = NULL; /*!< Device->Wprsnpr messages */ - char *_topic_signal_i2c_brkr = NULL; /*!< Topic carries messages from a device - to a broker. */ - char *_topic_signal_i2c_device = NULL; /*!< Topic carries messages from a - broker to a device. */ - char *_topic_signal_servo_brkr = NULL; /*!< Topic carries messages from a - device to a broker. */ - char *_topic_signal_servo_device = NULL; /*!< Topic carries messages from a - broker to a device. */ - char *_topic_signal_pwm_brkr = - NULL; /*!< Topic carries PWM messages from a device to a broker. */ - char *_topic_signal_pwm_device = - NULL; /*!< Topic carries PWM messages from a broker to a device. */ - char *_topic_signal_ds18_brkr = NULL; /*!< Topic carries ds18x20 messages from - a device to a broker. */ - char *_topic_signal_ds18_device = NULL; /*!< Topic carries ds18x20 messages - from a broker to a device. */ - char *_topic_signal_pixels_brkr = NULL; /*!< Topic carries pixel messages */ - char *_topic_signal_pixels_device = NULL; /*!< Topic carries pixel messages */ - char *_topic_signal_uart_brkr = NULL; /*!< Topic carries UART messages */ - char *_topic_signal_uart_device = NULL; /*!< Topic carries UART messages */ - - wippersnapper_signal_v1_CreateSignalRequest - _incomingSignalMsg; /*!< Incoming signal message from broker */ - wippersnapper_signal_v1_I2CRequest msgSignalI2C = - wippersnapper_signal_v1_I2CRequest_init_zero; ///< I2C request wrapper - ///< message - - // ds signal msg - wippersnapper_signal_v1_Ds18x20Request msgSignalDS = - wippersnapper_signal_v1_Ds18x20Request_init_zero; ///< DS request message - ///< wrapper - - // servo message - wippersnapper_signal_v1_ServoRequest - msgServo; ///< ServoRequest wrapper message - wippersnapper_signal_v1_PWMRequest msgPWM = - wippersnapper_signal_v1_PWMRequest_init_zero; ///< PWM request wrapper - ///< message. - - // pixels signal message - wippersnapper_signal_v1_PixelsRequest - msgPixels; ///< PixelsRequest wrapper message - - wippersnapper_signal_v1_UARTRequest - msgSignalUART; ///< UARTReq wrapper message - - char *throttleMessage; /*!< Pointer to throttle message data. */ - int throttleTime; /*!< Total amount of time to throttle the device, in - milliseconds. */ - - bool pinCfgCompleted = false; /*!< Did initial pin sync complete? */ - -// enable LEDC if esp32 -#ifdef ARDUINO_ARCH_ESP32 - ws_ledc *_ledc = nullptr; ///< Pointer to LEDC object -#endif - -private: - void _init(); - -protected: - ws_status_t _status = WS_IDLE; /*!< Adafruit IO connection status */ - uint32_t _last_mqtt_connect = 0; /*!< Previous time when client connected to - Adafruit IO, in milliseconds. */ - uint32_t _prv_ping = 0; /*!< Previous time when client pinged Adafruit IO's - MQTT broker, in milliseconds. */ - uint32_t _prvKATBlink = 0; /*!< Previous time when client pinged Adafruit IO's - MQTT broker, in milliseconds. */ - - // Device information - const char *_deviceId; /*!< Adafruit IO+ device identifier string */ - char *_device_uid; /*!< Unique device identifier */ - - // MQTT topics - char *_topic_description_status = - NULL; /*!< MQTT subtopic carrying the description - status resp. from the broker */ - char *_topic_description_status_complete = NULL; /*!< MQTT topic carrying the - ACK signal from the device to the - broker after registration */ - char *_topic_device_pin_config_complete = - NULL; /*!< MQTT topic carrying the ACK signal - from the device to the broker after - hardware configuration */ - char *_topic_signal_brkr = NULL; /*!< Wprsnpr->Device messages */ - char *_err_topic = NULL; /*!< Adafruit IO MQTT error message topic. */ - char *_throttle_topic = NULL; /*!< Adafruit IO MQTT throttle message topic. */ - - Adafruit_MQTT_Subscribe *_topic_description_sub; /*!< Subscription callback - for registration topic. */ - Adafruit_MQTT_Publish *_topic_signal_device_pub; /*!< Subscription callback - for D2C signal topic. */ - Adafruit_MQTT_Subscribe *_topic_signal_brkr_sub; /*!< Subscription callback - for C2D signal topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_i2c_sub; /*!< Subscription callback for I2C topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_servo_sub; /*!< Subscription callback for servo topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_pwm_sub; /*!< Subscription callback for pwm topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_ds18_sub; /*!< Subscribes to signal's ds18x20 topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_pixels_sub; /*!< Subscribes to pixel device topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_uart_sub; /*!< Subscribes to signal's UART topic. */ - - Adafruit_MQTT_Subscribe - *_err_sub; /*!< Subscription to Adafruit IO Error topic. */ - Adafruit_MQTT_Subscribe - *_throttle_sub; /*!< Subscription to Adafruit IO Throttle topic. */ - - wippersnapper_signal_v1_CreateSignalRequest - _outgoingSignalMsg; /*!< Outgoing signal message from device */ -}; -extern Wippersnapper WS; ///< Global member variable for callbacks - -#endif // ADAFRUIT_WIPPERSNAPPER_H +/*! + * @file Wippersnapper.h + * + * This is the documentation for Adafruit's Wippersnapper firmware for the + * Arduino platform. It is designed specifically to work with + * Adafruit IO Wippersnapper IoT platform. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2020-2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#ifndef WIPPERSNAPPER_H +#define WIPPERSNAPPER_H + +// Cpp STD +#include + +// Nanopb dependencies +#include +#include +#include +#include + +#include // description.proto +#include // signal.proto + +// External libraries +#include "Adafruit_MQTT.h" // MQTT Client +#include "Adafruit_SleepyDog.h" // Watchdog +#include "Arduino.h" // Wiring +#include // SPI + +// Wippersnapper API Helpers +#include "Wippersnapper_Boards.h" +#include "components/statusLED/Wippersnapper_StatusLED.h" +#include "provisioning/ConfigJson.h" + +#define WS_DEBUG ///< Define to enable debugging to serial terminal +#define WS_PRINTER Serial ///< Where debug messages will be printed + +// Define actual debug output functions when necessary. +#ifdef WS_DEBUG +#define WS_DEBUG_PRINT(...) \ + { WS_PRINTER.print(__VA_ARGS__); } ///< Prints debug output. +#define WS_DEBUG_PRINTLN(...) \ + { WS_PRINTER.println(__VA_ARGS__); } ///< Prints line from debug output. +#define WS_DEBUG_PRINTHEX(...) \ + { WS_PRINTER.print(__VA_ARGS__, HEX); } ///< Prints debug output. +#else +#define WS_DEBUG_PRINT(...) \ + {} ///< Prints debug output +#define WS_DEBUG_PRINTLN(...) \ + {} ///< 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" +#include "components/i2c/WipperSnapper_I2C.h" + +// LEDC-Manager, ESP32-only +#ifdef ARDUINO_ARCH_ESP32 +#include "components/ledc/ws_ledc.h" +#endif + +// Display +#ifdef USE_DISPLAY +#include "display/ws_display_driver.h" +#include "display/ws_display_ui_helper.h" +#endif + +#include "components/ds18x20/ws_ds18x20.h" +#include "components/pixels/ws_pixels.h" +#include "components/pwm/ws_pwm.h" +#include "components/servo/ws_servo.h" +#include "components/uart/ws_uart.h" + +#if defined(USE_TINYUSB) +#include "provisioning/tinyusb/Wippersnapper_FS.h" +#endif + +#if defined(USE_LITTLEFS) +#include "provisioning/littlefs/WipperSnapper_LittleFS.h" +#endif + +#define WS_VERSION \ + "1.0.0-beta.85" ///< WipperSnapper app. version (semver-formatted) + +// Reserved Adafruit IO MQTT topics +#define TOPIC_IO_THROTTLE "/throttle" ///< Adafruit IO Throttle MQTT Topic +#define TOPIC_IO_ERRORS "/errors" ///< Adafruit IO Error MQTT Topic + +// Reserved Wippersnapper topics +#define TOPIC_WS "/wprsnpr/" ///< WipperSnapper topic +#define TOPIC_INFO "/info/" ///< Registration sub-topic +#define TOPIC_SIGNALS "/signals/" ///< Signals sub-topic +#define TOPIC_I2C "/i2c" ///< I2C sub-topic +#define MQTT_TOPIC_PIXELS_DEVICE \ + "/signals/device/pixel" ///< Pixels device->broker topic +#define MQTT_TOPIC_PIXELS_BROKER \ + "/signals/broker/pixel" ///< Pixels broker->device topic + +/** Defines the Adafruit IO connection status */ +typedef enum { + WS_IDLE = 0, // Waiting for connection establishement + WS_NET_DISCONNECTED = 1, // Network disconnected + WS_DISCONNECTED = 2, // Disconnected from Adafruit IO + WS_FINGERPRINT_UNKOWN = 3, // Unknown WS_SSL_FINGERPRINT + + WS_NET_CONNECT_FAILED = 10, // Failed to connect to network + WS_CONNECT_FAILED = 11, // Failed to connect to Adafruit IO + WS_FINGERPRINT_INVALID = 12, // Unknown WS_SSL_FINGERPRINT + WS_AUTH_FAILED = 13, // Invalid Adafruit IO login credentials provided. + WS_SSID_INVALID = + 14, // SSID is "" or otherwise invalid, connection not attempted + + WS_NET_CONNECTED = 20, // Connected to Adafruit IO + WS_CONNECTED = 21, // Connected to network + WS_CONNECTED_INSECURE = 22, // Insecurely (non-SSL) connected to network + WS_FINGERPRINT_UNSUPPORTED = 23, // Unsupported WS_SSL_FINGERPRINT + WS_FINGERPRINT_VALID = 24, // Valid WS_SSL_FINGERPRINT + WS_BOARD_DESC_INVALID = 25, // Unable to send board description + WS_BOARD_RESYNC_FAILED = 26 // Board sync failure +} ws_status_t; + +/** Defines the Adafruit IO MQTT broker's connection return codes */ +typedef enum { + WS_MQTT_CONNECTED = 0, // Connected + WS_MQTT_INVALID_PROTOCOL = 1, // Invalid mqtt protocol + WS_MQTT_INVALID_CID = 2, // Client id rejected + WS_MQTT_SERVICE_UNAVALIABLE = 3, // Malformed user/pass + WS_MQTT_INVALID_USER_PASS = 4, // Unauthorized access to resource + WS_MQTT_UNAUTHORIZED = 5, // MQTT service unavailable + WS_MQTT_THROTTLED = 6, // Account throttled + WS_MQTT_BANNED = 7 // Account banned +} ws_mqtt_status_t; + +/** Defines the Wippersnapper client's hardware registration status */ +typedef enum { + WS_BOARD_DEF_IDLE, + WS_BOARD_DEF_SEND_FAILED, + WS_BOARD_DEF_SENT, + WS_BOARD_DEF_OK, + WS_BOARD_DEF_INVALID, + WS_BOARD_DEF_UNSPECIFIED +} ws_board_status_t; + +/** Defines the Wippersnapper client's network status */ +typedef enum { + FSM_NET_IDLE, + FSM_NET_CONNECTED, + FSM_MQTT_CONNECTED, + FSM_NET_CHECK_MQTT, + FSM_NET_CHECK_NETWORK, + FSM_NET_ESTABLISH_NETWORK, + FSM_NET_ESTABLISH_MQTT, +} fsm_net_t; + +#define WS_WDT_TIMEOUT 60000 ///< WDT timeout +/* MQTT Configuration */ +#define WS_KEEPALIVE_INTERVAL_MS \ + 5000 ///< Session keepalive interval time, in milliseconds + +#define WS_MQTT_MAX_PAYLOAD_SIZE \ + 512 ///< MAXIMUM expected payload size, in bytes + +class Wippersnapper_DigitalGPIO; +class Wippersnapper_AnalogIO; +class Wippersnapper_FS; +class WipperSnapper_LittleFS; +#ifdef USE_DISPLAY +class ws_display_driver; +class ws_display_ui_helper; +#endif +#ifdef ARDUINO_ARCH_ESP32 +class ws_ledc; +#endif +class WipperSnapper_Component_I2C; +class ws_servo; +class ws_pwm; +class ws_ds18x20; +class ws_pixels; +class ws_uart; + +/**************************************************************************/ +/*! + @brief Class that provides storage and functions for the Adafruit IO + Wippersnapper interface. +*/ +/**************************************************************************/ +class Wippersnapper { +public: + Wippersnapper(); + virtual ~Wippersnapper(); + + void provision(); + + bool lockStatusNeoPixel; ///< True if status LED is using the status neopixel + bool lockStatusDotStar; ///< True if status LED is using the status dotstar + bool lockStatusLED; ///< True if status LED is using the built-in LED + float status_pixel_brightness = + STATUS_PIXEL_BRIGHTNESS_DEFAULT; ///< Global status pixel's brightness + ///< (from 0.0 to 1.0) + + virtual void set_user_key(); + virtual void set_ssid_pass(const char *ssid, const char *ssidPassword); + virtual void set_ssid_pass(); + virtual bool check_valid_ssid(); + + virtual void _connect(); + virtual void _disconnect(); + void connect(); + void disconnect(); + + virtual void getMacAddr(); + virtual int32_t getRSSI(); + virtual void setupMQTTClient(const char *clientID); + + virtual ws_status_t networkStatus(); + ws_board_status_t getBoardStatus(); + + bool generateDeviceUID(); + bool generateWSTopics(); + bool generateWSErrorTopics(); + + // Registration API + bool registerBoard(); + bool encodePubRegistrationReq(); + void decodeRegistrationResp(char *data, uint16_t len); + void pollRegistrationResp(); + // Configuration API + void publishPinConfigComplete(); + + // run() loop + ws_status_t run(); + void processPackets(); + void publish(const char *topic, uint8_t *payload, uint16_t bLen, + uint8_t qos = 0); + + // Networking helpers + void pingBroker(); + void runNetFSM(); + + // WDT helpers + void enableWDT(int timeoutMS = 0); + void feedWDT(); + + // Error handling helpers + void haltError(String error, + ws_led_status_t ledStatusColor = WS_LED_STATUS_ERROR_RUNTIME); + void errorWriteHang(String error); + + // MQTT topic callbacks // + // Decodes a signal message + bool decodeSignalMsg( + wippersnapper_signal_v1_CreateSignalRequest *encodedSignalMsg); + + // Encodes a pin event message + bool + encodePinEvent(wippersnapper_signal_v1_CreateSignalRequest *outgoingSignalMsg, + uint8_t pinName, int pinVal); + + // Pin configure message + bool configureDigitalPinReq(wippersnapper_pin_v1_ConfigurePinRequest *pinMsg); + bool configAnalogInPinReq(wippersnapper_pin_v1_ConfigurePinRequest *pinMsg); + + // I2C + std::vector + i2cComponents; ///< Vector containing all I2C components + WipperSnapper_Component_I2C *_i2cPort0 = + NULL; ///< WipperSnapper I2C Component for I2C port #0 + WipperSnapper_Component_I2C *_i2cPort1 = + NULL; ///< WipperSnapper I2C Component for I2C port #1 + bool _isI2CPort0Init = + false; ///< True if I2C port 0 has been initialized, False otherwise. + bool _isI2CPort1Init = + false; ///< True if I2C port 1 has been initialized, False otherwise. + + uint8_t _buffer[WS_MQTT_MAX_PAYLOAD_SIZE]; /*!< Shared buffer to save callback + payload */ + uint8_t + _buffer_outgoing[WS_MQTT_MAX_PAYLOAD_SIZE]; /*!< buffer which contains + outgoing payload data */ + uint16_t bufSize; /*!< Length of data inside buffer */ + + ws_board_status_t _boardStatus = + WS_BOARD_DEF_IDLE; ///< Hardware's registration status + + // TODO: We really should look at making these static definitions, not dynamic + // to free up space on the heap + Wippersnapper_DigitalGPIO *_digitalGPIO; ///< Instance of digital gpio class + Wippersnapper_AnalogIO *_analogIO; ///< Instance of analog io class + Wippersnapper_FS *_fileSystem; ///< Instance of Filesystem (native USB) + WipperSnapper_LittleFS + *_littleFS; ///< Instance of LittleFS Filesystem (non-native USB) +#ifdef USE_DISPLAY + ws_display_driver *_display = nullptr; ///< Instance of display driver class + ws_display_ui_helper *_ui_helper = + nullptr; ///< Instance of display UI helper class +#endif + ws_pixels *_ws_pixelsComponent; ///< ptr to instance of ws_pixels class + ws_pwm *_pwmComponent; ///< Instance of pwm class + ws_servo *_servoComponent; ///< Instance of servo class + ws_ds18x20 *_ds18x20Component; ///< Instance of DS18x20 class + ws_uart *_uartComponent; ///< Instance of UART class + + // TODO: does this really need to be global? + uint8_t _macAddr[6]; /*!< Unique network iface identifier */ + char sUID[13]; /*!< Unique network iface identifier */ + const char *_boardId; /*!< Adafruit IO+ board string */ + Adafruit_MQTT *_mqtt; /*!< Reference to Adafruit_MQTT, _mqtt. */ + + secretsConfig _config; /*!< Wippersnapper secrets.json as a struct. */ + + // TODO: Does this need to be within this class? + int32_t totalDigitalPins; /*!< Total number of digital-input capable pins */ + + char *_topic_description = NULL; /*!< MQTT topic for the device description */ + char *_topic_signal_device = NULL; /*!< Device->Wprsnpr messages */ + char *_topic_signal_i2c_brkr = NULL; /*!< Topic carries messages from a device + to a broker. */ + char *_topic_signal_i2c_device = NULL; /*!< Topic carries messages from a + broker to a device. */ + char *_topic_signal_servo_brkr = NULL; /*!< Topic carries messages from a + device to a broker. */ + char *_topic_signal_servo_device = NULL; /*!< Topic carries messages from a + broker to a device. */ + char *_topic_signal_pwm_brkr = + NULL; /*!< Topic carries PWM messages from a device to a broker. */ + char *_topic_signal_pwm_device = + NULL; /*!< Topic carries PWM messages from a broker to a device. */ + char *_topic_signal_ds18_brkr = NULL; /*!< Topic carries ds18x20 messages from + a device to a broker. */ + char *_topic_signal_ds18_device = NULL; /*!< Topic carries ds18x20 messages + from a broker to a device. */ + char *_topic_signal_pixels_brkr = NULL; /*!< Topic carries pixel messages */ + char *_topic_signal_pixels_device = NULL; /*!< Topic carries pixel messages */ + char *_topic_signal_uart_brkr = NULL; /*!< Topic carries UART messages */ + char *_topic_signal_uart_device = NULL; /*!< Topic carries UART messages */ + + wippersnapper_signal_v1_CreateSignalRequest + _incomingSignalMsg; /*!< Incoming signal message from broker */ + wippersnapper_signal_v1_I2CRequest msgSignalI2C = + wippersnapper_signal_v1_I2CRequest_init_zero; ///< I2C request wrapper + ///< message + + // ds signal msg + wippersnapper_signal_v1_Ds18x20Request msgSignalDS = + wippersnapper_signal_v1_Ds18x20Request_init_zero; ///< DS request message + ///< wrapper + + // servo message + wippersnapper_signal_v1_ServoRequest + msgServo; ///< ServoRequest wrapper message + wippersnapper_signal_v1_PWMRequest msgPWM = + wippersnapper_signal_v1_PWMRequest_init_zero; ///< PWM request wrapper + ///< message. + + // pixels signal message + wippersnapper_signal_v1_PixelsRequest + msgPixels; ///< PixelsRequest wrapper message + + wippersnapper_signal_v1_UARTRequest + msgSignalUART; ///< UARTReq wrapper message + + char *throttleMessage; /*!< Pointer to throttle message data. */ + int throttleTime; /*!< Total amount of time to throttle the device, in + milliseconds. */ + + bool pinCfgCompleted = false; /*!< Did initial pin sync complete? */ + +// enable LEDC if esp32 +#ifdef ARDUINO_ARCH_ESP32 + ws_ledc *_ledc = nullptr; ///< Pointer to LEDC object +#endif + +private: + void _init(); + +protected: + ws_status_t _status = WS_IDLE; /*!< Adafruit IO connection status */ + uint32_t _last_mqtt_connect = 0; /*!< Previous time when client connected to + Adafruit IO, in milliseconds. */ + uint32_t _prv_ping = 0; /*!< Previous time when client pinged Adafruit IO's + MQTT broker, in milliseconds. */ + uint32_t _prvKATBlink = 0; /*!< Previous time when client pinged Adafruit IO's + MQTT broker, in milliseconds. */ + + // Device information + const char *_deviceId; /*!< Adafruit IO+ device identifier string */ + char *_device_uid; /*!< Unique device identifier */ + + // MQTT topics + char *_topic_description_status = + NULL; /*!< MQTT subtopic carrying the description + status resp. from the broker */ + char *_topic_description_status_complete = NULL; /*!< MQTT topic carrying the + ACK signal from the device to the + broker after registration */ + char *_topic_device_pin_config_complete = + NULL; /*!< MQTT topic carrying the ACK signal + from the device to the broker after + hardware configuration */ + char *_topic_signal_brkr = NULL; /*!< Wprsnpr->Device messages */ + char *_err_topic = NULL; /*!< Adafruit IO MQTT error message topic. */ + char *_throttle_topic = NULL; /*!< Adafruit IO MQTT throttle message topic. */ + + Adafruit_MQTT_Subscribe *_topic_description_sub; /*!< Subscription callback + for registration topic. */ + Adafruit_MQTT_Publish *_topic_signal_device_pub; /*!< Subscription callback + for D2C signal topic. */ + Adafruit_MQTT_Subscribe *_topic_signal_brkr_sub; /*!< Subscription callback + for C2D signal topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_i2c_sub; /*!< Subscription callback for I2C topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_servo_sub; /*!< Subscription callback for servo topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_pwm_sub; /*!< Subscription callback for pwm topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_ds18_sub; /*!< Subscribes to signal's ds18x20 topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_pixels_sub; /*!< Subscribes to pixel device topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_uart_sub; /*!< Subscribes to signal's UART topic. */ + + Adafruit_MQTT_Subscribe + *_err_sub; /*!< Subscription to Adafruit IO Error topic. */ + Adafruit_MQTT_Subscribe + *_throttle_sub; /*!< Subscription to Adafruit IO Throttle topic. */ + + wippersnapper_signal_v1_CreateSignalRequest + _outgoingSignalMsg; /*!< Outgoing signal message from device */ +}; +extern Wippersnapper WS; ///< Global member variable for callbacks + +#endif // ADAFRUIT_WIPPERSNAPPER_H diff --git a/src/components/analogIO/Wippersnapper_AnalogIO.cpp b/src/components/analogIO/Wippersnapper_AnalogIO.cpp index 4fb700bb4..a384cdba6 100644 --- a/src/components/analogIO/Wippersnapper_AnalogIO.cpp +++ b/src/components/analogIO/Wippersnapper_AnalogIO.cpp @@ -1,396 +1,396 @@ -/*! - * @file Wippersnapper_AnalogIO.cpp - * - * This file provides an API for interacting with - * a board's analog IO pins. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2020-2023 for Adafruit Industries. - * - * BSD license, all text here must be included in any redistribution. - * - */ - -#include "Wippersnapper_AnalogIO.h" - -/***********************************************************************************/ -/*! - @brief Initializes Analog IO class. - @param totalAnalogInputPins - Total number of analog input pins to allocate. - @param aRef - ADC's voltage reference value, in volts. -*/ -/***********************************************************************************/ -Wippersnapper_AnalogIO::Wippersnapper_AnalogIO(int32_t totalAnalogInputPins, - float aRef) { - _totalAnalogInputPins = totalAnalogInputPins; - - // Set aref - setAref(aRef); - - // Set ADC resolution, default to 16-bit - setADCResolution(16); - - // allocate analog input pins - _analog_input_pins = new analogInputPin[_totalAnalogInputPins]; - - // TODO: Refactor this to use list-based initialization - for (int pin = 0; pin < _totalAnalogInputPins; pin++) { - // turn sampling off - _analog_input_pins[pin].enabled = false; - } -} - -/***********************************************************************************/ -/*! - @brief Destructor for Analog IO class. -*/ -/***********************************************************************************/ -Wippersnapper_AnalogIO::~Wippersnapper_AnalogIO() { - _aRef = 0.0; - _totalAnalogInputPins = 0; - delete _analog_input_pins; -} - -/***********************************************************************************/ -/*! - @brief Sets the device's reference voltage. - @param refVoltage - The voltage reference to use during conversions. -*/ -/***********************************************************************************/ -void Wippersnapper_AnalogIO::setAref(float refVoltage) { _aRef = refVoltage; } - -/***********************************************************************************/ -/*! - @brief Returns the device's reference voltage. - @returns Analog reference voltage, in volts. -*/ -/***********************************************************************************/ -float Wippersnapper_AnalogIO::getAref() { return _aRef; } - -/***********************************************************************************/ -/*! - @brief Sets the device's ADC resolution, either natively via calling - Arduino API's analogReadResolution() or via scaling. - @param resolution - The desired analog resolution, in bits. -*/ -/***********************************************************************************/ -void Wippersnapper_AnalogIO::setADCResolution(int resolution) { -// set the resolution natively in the BSP -#if defined(ARDUINO_ARCH_SAMD) - analogReadResolution(16); - _nativeResolution = 12; -#elif defined(ARDUINO_ARCH_ESP32) - scaleAnalogRead = true; - _nativeResolution = 13; -#endif - - _adcResolution = resolution; -} - -/***********************************************************************************/ -/*! - @brief Gets the scaled ADC resolution. - @returns resolution - The scaled analog resolution, in bits. -*/ -/***********************************************************************************/ -int Wippersnapper_AnalogIO::getADCresolution() { return _adcResolution; } - -/***********************************************************************************/ -/*! - @brief Gets the device's native ADC resolution. - @returns resolution - The native analog resolution, in bits. -*/ -/***********************************************************************************/ -int Wippersnapper_AnalogIO::getNativeResolution() { return _nativeResolution; } - -/***********************************************************************************/ -/*! - @brief Initializes an analog input pin - @param pin - The analog pin to read from. - @param period - Time between measurements, in seconds. - @param pullMode - The pin's pull value. - @param analogReadMode - Defines if pin will read and return an ADC value or a voltage value. -*/ -/***********************************************************************************/ -void Wippersnapper_AnalogIO::initAnalogInputPin( - int pin, float period, - wippersnapper_pin_v1_ConfigurePinRequest_Pull pullMode, - wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode analogReadMode) { - - // Set analog read pull mode - if (pullMode == wippersnapper_pin_v1_ConfigurePinRequest_Pull_PULL_UP) - pinMode(pin, INPUT_PULLUP); - else - pinMode(pin, INPUT); - - // Period is in seconds, cast it to long and convert it to milliseconds - long periodMs = (long)period * 1000; - - // TODO: Maybe pull this out into a func. or use map() lookup instead - // attempt to allocate pin within _analog_input_pins[] - for (int i = 0; i < _totalAnalogInputPins; i++) { - if (_analog_input_pins[i].enabled == false) { - _analog_input_pins[i].pinName = pin; - _analog_input_pins[i].period = periodMs; - _analog_input_pins[i].prvPeriod = 0L; - _analog_input_pins[i].readMode = analogReadMode; - _analog_input_pins[i].enabled = true; - break; - } - } - WS_DEBUG_PRINT("Configured Analog Input pin with polling time (ms):"); - WS_DEBUG_PRINTLN(periodMs); -} - -/***********************************************************************************/ -/*! - @brief Disables an analog input pin from sampling - @param pin - The analog input pin to disable. -*/ -/***********************************************************************************/ -void Wippersnapper_AnalogIO::disableAnalogInPin(int pin) { - for (int i = 0; i < _totalAnalogInputPins; i++) { - if (_analog_input_pins[i].pinName == pin) { - _analog_input_pins[i].enabled = false; - break; - } - } -} - -/***********************************************************************************/ -/*! - @brief Deinitializes an analog pin. - @param direction - The analog pin's direction. - @param pin - The analog pin to deinitialize. - -*/ -/***********************************************************************************/ -void Wippersnapper_AnalogIO::deinitAnalogPin( - wippersnapper_pin_v1_ConfigurePinRequest_Direction direction, int pin) { - WS_DEBUG_PRINT("Deinitializing analog pin A"); - WS_DEBUG_PRINTLN(pin); - if (direction == - wippersnapper_pin_v1_ConfigurePinRequest_Direction_DIRECTION_INPUT) { - WS_DEBUG_PRINTLN("Deinitialized analog input pin obj."); - disableAnalogInPin(pin); - } - pinMode(pin, INPUT); // hi-z -} - -/**********************************************************/ -/*! - @brief Reads the raw ADC value of an analog pin. - Value is always scaled to 16-bit. - @param pin - The pin to be read. - @returns The pin's ADC value. -*/ -/**********************************************************/ -uint16_t Wippersnapper_AnalogIO::getPinValue(int pin) { - // get pin value - uint16_t value = analogRead(pin); - - // scale by the ADC resolution manually if not implemented by BSP - if (scaleAnalogRead) { - if (getADCresolution() > getNativeResolution()) { - value = value << (getADCresolution() - getNativeResolution()); - } else { - value = value >> (getNativeResolution() - getADCresolution()); - } - } - return value; -} - -/**********************************************************/ -/*! - @brief Calculates analog pin's voltage provided - a 16-bit ADC value. - @param pin - The value from a previous ADC reading. - @returns The pin's voltage. -*/ -/**********************************************************/ -float Wippersnapper_AnalogIO::getPinValueVolts(int pin) { - uint16_t rawValue = getPinValue(pin); - return rawValue * getAref() / 65536; -} - -/******************************************************************/ -/*! - @brief Encodes an analog input pin event into a - signal message and publish it to IO. - @param pinName - Specifies the pin's name. - @param readMode - Read mode - raw ADC or voltage. - @param pinValRaw - Raw pin value, used if readmode is raw. - @param pinValVolts - Raw pin value expressed in Volts, used if readmode is - volts. - @returns True if successfully encoded a PinEvent signal - message, False otherwise. -*/ -/******************************************************************/ -bool Wippersnapper_AnalogIO::encodePinEvent( - uint8_t pinName, - wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode readMode, - uint16_t pinValRaw, float pinValVolts) { - // Create new signal message - wippersnapper_signal_v1_CreateSignalRequest outgoingSignalMsg = - wippersnapper_signal_v1_CreateSignalRequest_init_zero; - - // Fill payload - outgoingSignalMsg.which_payload = - wippersnapper_signal_v1_CreateSignalRequest_pin_event_tag; - sprintf(outgoingSignalMsg.payload.pin_event.pin_name, "A%d", pinName); - - // Fill pinValue based on the analog read mode - char buffer[100]; - if (readMode == - wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VALUE) { - sprintf(outgoingSignalMsg.payload.pin_event.pin_value, "%u", pinValRaw); - snprintf(buffer, 100, "[Pin] A%d read: %u\n", pinName, pinValRaw); - } else { - sprintf(outgoingSignalMsg.payload.pin_event.pin_value, "%0.3f", - pinValVolts); - snprintf(buffer, 100, "[Pin] A%d read: %0.2f\n", pinName, pinValVolts); - } -// display analog pin read on terminal -#ifdef USE_DISPLAY - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - // Encode signal message - pb_ostream_t stream = - pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!ws_pb_encode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, - &outgoingSignalMsg)) { - WS_DEBUG_PRINTLN("ERROR: Unable to encode signal message"); - return false; - } - - // Publish out to IO - size_t msgSz; - pb_get_encoded_size(&msgSz, - wippersnapper_signal_v1_CreateSignalRequest_fields, - &outgoingSignalMsg); - WS_DEBUG_PRINT("Publishing pinEvent..."); - WS.publish(WS._topic_signal_device, WS._buffer_outgoing, msgSz, 1); - WS_DEBUG_PRINTLN("Published!"); - - return true; -} - -/**********************************************************/ -/*! - @brief Checks if pin's period is expired. - @param currentTime - The current software timer value. - @param pin - The desired analog pin to check - @returns True if pin's period expired, False otherwise. -*/ -/**********************************************************/ -bool Wippersnapper_AnalogIO::timerExpired(long currentTime, - analogInputPin pin) { - if (currentTime - pin.prvPeriod > pin.period && pin.period != 0L) - return true; - return false; -} - -/**********************************************************/ -/*! - @brief Iterates thru analog inputs -*/ -/**********************************************************/ -void Wippersnapper_AnalogIO::update() { - // TODO: Globally scope these, dont have them here every time - float pinValVolts = 0.0; - uint16_t pinValRaw = 0; - // Process analog input pins - for (int i = 0; i < _totalAnalogInputPins; i++) { - // TODO: Can we collapse the conditionals below? - if (_analog_input_pins[i].enabled == true) { - - // Does the pin execute on-period? - if ((long)millis() - _analog_input_pins[i].prvPeriod > - _analog_input_pins[i].period && - _analog_input_pins[i].period != 0L) { - WS_DEBUG_PRINT("Executing periodic event on A"); - WS_DEBUG_PRINTLN(_analog_input_pins[i].pinName); - - // Read from analog pin - if (_analog_input_pins[i].readMode == - wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VOLTAGE) { - pinValVolts = getPinValueVolts(_analog_input_pins[i].pinName); - } else if ( - _analog_input_pins[i].readMode == - wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VALUE) { - pinValRaw = getPinValue(_analog_input_pins[i].pinName); - } else { - WS_DEBUG_PRINTLN("ERROR: Unable to read pin value, cannot determine " - "analog read mode!"); - pinValRaw = 0.0; - } - - // Publish a new pin event - encodePinEvent(_analog_input_pins[i].pinName, - _analog_input_pins[i].readMode, pinValRaw, pinValVolts); - - // IMPT - reset the digital pin - _analog_input_pins[i].prvPeriod = millis(); - } - // Does the pin execute on_change? - else if (_analog_input_pins[i].period == 0L) { - - // note: on-change requires ADC DEFAULT_HYSTERISIS to check against prv - // pin value - uint16_t pinValRaw = getPinValue(_analog_input_pins[i].pinName); - - uint16_t _pinValThreshHi = - _analog_input_pins[i].prvPinVal + - (_analog_input_pins[i].prvPinVal * DEFAULT_HYSTERISIS); - uint16_t _pinValThreshLow = - _analog_input_pins[i].prvPinVal - - (_analog_input_pins[i].prvPinVal * DEFAULT_HYSTERISIS); - - if (pinValRaw > _pinValThreshHi || pinValRaw < _pinValThreshLow) { - // Perform voltage conversion if we need to - if (_analog_input_pins[i].readMode == - wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VOLTAGE) { - pinValVolts = pinValRaw * getAref() / 65536; - } - - // Publish pin event to IO - encodePinEvent(_analog_input_pins[i].pinName, - _analog_input_pins[i].readMode, pinValRaw, - pinValVolts); - - } else { - // WS_DEBUG_PRINTLN("ADC has not changed enough, continue..."); - continue; - } - // set the pin value in the digital pin object for comparison on next - // run - _analog_input_pins[i].prvPinVal = pinValRaw; - } - } - } +/*! + * @file Wippersnapper_AnalogIO.cpp + * + * This file provides an API for interacting with + * a board's analog IO pins. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2020-2023 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#include "Wippersnapper_AnalogIO.h" + +/***********************************************************************************/ +/*! + @brief Initializes Analog IO class. + @param totalAnalogInputPins + Total number of analog input pins to allocate. + @param aRef + ADC's voltage reference value, in volts. +*/ +/***********************************************************************************/ +Wippersnapper_AnalogIO::Wippersnapper_AnalogIO(int32_t totalAnalogInputPins, + float aRef) { + _totalAnalogInputPins = totalAnalogInputPins; + + // Set aref + setAref(aRef); + + // Set ADC resolution, default to 16-bit + setADCResolution(16); + + // allocate analog input pins + _analog_input_pins = new analogInputPin[_totalAnalogInputPins]; + + // TODO: Refactor this to use list-based initialization + for (int pin = 0; pin < _totalAnalogInputPins; pin++) { + // turn sampling off + _analog_input_pins[pin].enabled = false; + } +} + +/***********************************************************************************/ +/*! + @brief Destructor for Analog IO class. +*/ +/***********************************************************************************/ +Wippersnapper_AnalogIO::~Wippersnapper_AnalogIO() { + _aRef = 0.0; + _totalAnalogInputPins = 0; + delete _analog_input_pins; +} + +/***********************************************************************************/ +/*! + @brief Sets the device's reference voltage. + @param refVoltage + The voltage reference to use during conversions. +*/ +/***********************************************************************************/ +void Wippersnapper_AnalogIO::setAref(float refVoltage) { _aRef = refVoltage; } + +/***********************************************************************************/ +/*! + @brief Returns the device's reference voltage. + @returns Analog reference voltage, in volts. +*/ +/***********************************************************************************/ +float Wippersnapper_AnalogIO::getAref() { return _aRef; } + +/***********************************************************************************/ +/*! + @brief Sets the device's ADC resolution, either natively via calling + Arduino API's analogReadResolution() or via scaling. + @param resolution + The desired analog resolution, in bits. +*/ +/***********************************************************************************/ +void Wippersnapper_AnalogIO::setADCResolution(int resolution) { +// set the resolution natively in the BSP +#if defined(ARDUINO_ARCH_SAMD) + analogReadResolution(16); + _nativeResolution = 12; +#elif defined(ARDUINO_ARCH_ESP32) + scaleAnalogRead = true; + _nativeResolution = 13; +#endif + + _adcResolution = resolution; +} + +/***********************************************************************************/ +/*! + @brief Gets the scaled ADC resolution. + @returns resolution + The scaled analog resolution, in bits. +*/ +/***********************************************************************************/ +int Wippersnapper_AnalogIO::getADCresolution() { return _adcResolution; } + +/***********************************************************************************/ +/*! + @brief Gets the device's native ADC resolution. + @returns resolution + The native analog resolution, in bits. +*/ +/***********************************************************************************/ +int Wippersnapper_AnalogIO::getNativeResolution() { return _nativeResolution; } + +/***********************************************************************************/ +/*! + @brief Initializes an analog input pin + @param pin + The analog pin to read from. + @param period + Time between measurements, in seconds. + @param pullMode + The pin's pull value. + @param analogReadMode + Defines if pin will read and return an ADC value or a voltage value. +*/ +/***********************************************************************************/ +void Wippersnapper_AnalogIO::initAnalogInputPin( + int pin, float period, + wippersnapper_pin_v1_ConfigurePinRequest_Pull pullMode, + wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode analogReadMode) { + + // Set analog read pull mode + if (pullMode == wippersnapper_pin_v1_ConfigurePinRequest_Pull_PULL_UP) + pinMode(pin, INPUT_PULLUP); + else + pinMode(pin, INPUT); + + // Period is in seconds, cast it to long and convert it to milliseconds + long periodMs = (long)period * 1000; + + // TODO: Maybe pull this out into a func. or use map() lookup instead + // attempt to allocate pin within _analog_input_pins[] + for (int i = 0; i < _totalAnalogInputPins; i++) { + if (_analog_input_pins[i].enabled == false) { + _analog_input_pins[i].pinName = pin; + _analog_input_pins[i].period = periodMs; + _analog_input_pins[i].prvPeriod = 0L; + _analog_input_pins[i].readMode = analogReadMode; + _analog_input_pins[i].enabled = true; + break; + } + } + WS_DEBUG_PRINT("Configured Analog Input pin with polling time (ms):"); + WS_DEBUG_PRINTLN(periodMs); +} + +/***********************************************************************************/ +/*! + @brief Disables an analog input pin from sampling + @param pin + The analog input pin to disable. +*/ +/***********************************************************************************/ +void Wippersnapper_AnalogIO::disableAnalogInPin(int pin) { + for (int i = 0; i < _totalAnalogInputPins; i++) { + if (_analog_input_pins[i].pinName == pin) { + _analog_input_pins[i].enabled = false; + break; + } + } +} + +/***********************************************************************************/ +/*! + @brief Deinitializes an analog pin. + @param direction + The analog pin's direction. + @param pin + The analog pin to deinitialize. + +*/ +/***********************************************************************************/ +void Wippersnapper_AnalogIO::deinitAnalogPin( + wippersnapper_pin_v1_ConfigurePinRequest_Direction direction, int pin) { + WS_DEBUG_PRINT("Deinitializing analog pin A"); + WS_DEBUG_PRINTLN(pin); + if (direction == + wippersnapper_pin_v1_ConfigurePinRequest_Direction_DIRECTION_INPUT) { + WS_DEBUG_PRINTLN("Deinitialized analog input pin obj."); + disableAnalogInPin(pin); + } + pinMode(pin, INPUT); // hi-z +} + +/**********************************************************/ +/*! + @brief Reads the raw ADC value of an analog pin. + Value is always scaled to 16-bit. + @param pin + The pin to be read. + @returns The pin's ADC value. +*/ +/**********************************************************/ +uint16_t Wippersnapper_AnalogIO::getPinValue(int pin) { + // get pin value + uint16_t value = analogRead(pin); + + // scale by the ADC resolution manually if not implemented by BSP + if (scaleAnalogRead) { + if (getADCresolution() > getNativeResolution()) { + value = value << (getADCresolution() - getNativeResolution()); + } else { + value = value >> (getNativeResolution() - getADCresolution()); + } + } + return value; +} + +/**********************************************************/ +/*! + @brief Calculates analog pin's voltage provided + a 16-bit ADC value. + @param pin + The value from a previous ADC reading. + @returns The pin's voltage. +*/ +/**********************************************************/ +float Wippersnapper_AnalogIO::getPinValueVolts(int pin) { + uint16_t rawValue = getPinValue(pin); + return rawValue * getAref() / 65536; +} + +/******************************************************************/ +/*! + @brief Encodes an analog input pin event into a + signal message and publish it to IO. + @param pinName + Specifies the pin's name. + @param readMode + Read mode - raw ADC or voltage. + @param pinValRaw + Raw pin value, used if readmode is raw. + @param pinValVolts + Raw pin value expressed in Volts, used if readmode is + volts. + @returns True if successfully encoded a PinEvent signal + message, False otherwise. +*/ +/******************************************************************/ +bool Wippersnapper_AnalogIO::encodePinEvent( + uint8_t pinName, + wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode readMode, + uint16_t pinValRaw, float pinValVolts) { + // Create new signal message + wippersnapper_signal_v1_CreateSignalRequest outgoingSignalMsg = + wippersnapper_signal_v1_CreateSignalRequest_init_zero; + + // Fill payload + outgoingSignalMsg.which_payload = + wippersnapper_signal_v1_CreateSignalRequest_pin_event_tag; + sprintf(outgoingSignalMsg.payload.pin_event.pin_name, "A%d", pinName); + + // Fill pinValue based on the analog read mode + char buffer[100]; + if (readMode == + wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VALUE) { + sprintf(outgoingSignalMsg.payload.pin_event.pin_value, "%u", pinValRaw); + snprintf(buffer, 100, "[Pin] A%d read: %u\n", pinName, pinValRaw); + } else { + sprintf(outgoingSignalMsg.payload.pin_event.pin_value, "%0.3f", + pinValVolts); + snprintf(buffer, 100, "[Pin] A%d read: %0.2f\n", pinName, pinValVolts); + } +// display analog pin read on terminal +#ifdef USE_DISPLAY + WS._ui_helper->add_text_to_terminal(buffer); +#endif + + // Encode signal message + pb_ostream_t stream = + pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); + if (!ws_pb_encode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, + &outgoingSignalMsg)) { + WS_DEBUG_PRINTLN("ERROR: Unable to encode signal message"); + return false; + } + + // Publish out to IO + size_t msgSz; + pb_get_encoded_size(&msgSz, + wippersnapper_signal_v1_CreateSignalRequest_fields, + &outgoingSignalMsg); + WS_DEBUG_PRINT("Publishing pinEvent..."); + WS.publish(WS._topic_signal_device, WS._buffer_outgoing, msgSz, 1); + WS_DEBUG_PRINTLN("Published!"); + + return true; +} + +/**********************************************************/ +/*! + @brief Checks if pin's period is expired. + @param currentTime + The current software timer value. + @param pin + The desired analog pin to check + @returns True if pin's period expired, False otherwise. +*/ +/**********************************************************/ +bool Wippersnapper_AnalogIO::timerExpired(long currentTime, + analogInputPin pin) { + if (currentTime - pin.prvPeriod > pin.period && pin.period != 0L) + return true; + return false; +} + +/**********************************************************/ +/*! + @brief Iterates thru analog inputs +*/ +/**********************************************************/ +void Wippersnapper_AnalogIO::update() { + // TODO: Globally scope these, dont have them here every time + float pinValVolts = 0.0; + uint16_t pinValRaw = 0; + // Process analog input pins + for (int i = 0; i < _totalAnalogInputPins; i++) { + // TODO: Can we collapse the conditionals below? + if (_analog_input_pins[i].enabled == true) { + + // Does the pin execute on-period? + if ((long)millis() - _analog_input_pins[i].prvPeriod > + _analog_input_pins[i].period && + _analog_input_pins[i].period != 0L) { + WS_DEBUG_PRINT("Executing periodic event on A"); + WS_DEBUG_PRINTLN(_analog_input_pins[i].pinName); + + // Read from analog pin + if (_analog_input_pins[i].readMode == + wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VOLTAGE) { + pinValVolts = getPinValueVolts(_analog_input_pins[i].pinName); + } else if ( + _analog_input_pins[i].readMode == + wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VALUE) { + pinValRaw = getPinValue(_analog_input_pins[i].pinName); + } else { + WS_DEBUG_PRINTLN("ERROR: Unable to read pin value, cannot determine " + "analog read mode!"); + pinValRaw = 0.0; + } + + // Publish a new pin event + encodePinEvent(_analog_input_pins[i].pinName, + _analog_input_pins[i].readMode, pinValRaw, pinValVolts); + + // IMPT - reset the digital pin + _analog_input_pins[i].prvPeriod = millis(); + } + // Does the pin execute on_change? + else if (_analog_input_pins[i].period == 0L) { + + // note: on-change requires ADC DEFAULT_HYSTERISIS to check against prv + // pin value + uint16_t pinValRaw = getPinValue(_analog_input_pins[i].pinName); + + uint16_t _pinValThreshHi = + _analog_input_pins[i].prvPinVal + + (_analog_input_pins[i].prvPinVal * DEFAULT_HYSTERISIS); + uint16_t _pinValThreshLow = + _analog_input_pins[i].prvPinVal - + (_analog_input_pins[i].prvPinVal * DEFAULT_HYSTERISIS); + + if (pinValRaw > _pinValThreshHi || pinValRaw < _pinValThreshLow) { + // Perform voltage conversion if we need to + if (_analog_input_pins[i].readMode == + wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VOLTAGE) { + pinValVolts = pinValRaw * getAref() / 65536; + } + + // Publish pin event to IO + encodePinEvent(_analog_input_pins[i].pinName, + _analog_input_pins[i].readMode, pinValRaw, + pinValVolts); + + } else { + // WS_DEBUG_PRINTLN("ADC has not changed enough, continue..."); + continue; + } + // set the pin value in the digital pin object for comparison on next + // run + _analog_input_pins[i].prvPinVal = pinValRaw; + } + } + } } \ No newline at end of file diff --git a/src/components/ds18x20/ws_ds18x20.cpp b/src/components/ds18x20/ws_ds18x20.cpp index 39c7e38df..0cfa6e0c5 100644 --- a/src/components/ds18x20/ws_ds18x20.cpp +++ b/src/components/ds18x20/ws_ds18x20.cpp @@ -1,313 +1,313 @@ -/*! - * @file ws_ds18x20.cpp - * - * This component implements 1-wire communication - * for the DS18X20-line of Maxim Temperature ICs. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2022-2023 for Adafruit Industries. - * - * BSD license, all text here must be included in any redistribution. - * - */ - -#include "ws_ds18x20.h" - -/*************************************************************/ -/*! - @brief Creates a new WipperSnapper Ds18x20 component. -*/ -/*************************************************************/ -ws_ds18x20::ws_ds18x20() {} - -/*************************************************************/ -/*! - @brief Destructor for a WipperSnapper DS18X20 component. -*/ -/*************************************************************/ -ws_ds18x20::~ws_ds18x20() { - // delete DallasTemp sensors and release onewire buses - for (size_t idx = 0; idx < _ds18xDrivers.size(); idx++) { - delete _ds18xDrivers[idx]->dallasTempObj; - delete _ds18xDrivers[idx]->oneWire; - } - // remove all elements - _ds18xDrivers.clear(); -} - -/********************************************************************/ -/*! - @brief Initializes a DS18x20 sensor using a - configuration sent by the broker and adds it to a - vector of ds18x20 sensor drivers. - @param msgDs18x20InitReq - Message containing configuration data for a - ds18x20 sensor. - @returns True if initialized successfully, False otherwise. -*/ -/********************************************************************/ -bool ws_ds18x20::addDS18x20( - wippersnapper_ds18x20_v1_Ds18x20InitRequest *msgDs18x20InitReq) { - bool is_success = false; - - // init. new ds18x20 object - ds18x20Obj *newObj = new ds18x20Obj(); - char *oneWirePin = msgDs18x20InitReq->onewire_pin + 1; - newObj->oneWire = new OneWire(atoi(oneWirePin)); - newObj->dallasTempObj = new DallasTemperature(newObj->oneWire); - newObj->dallasTempObj->begin(); - // attempt to obtain sensor address - if (newObj->dallasTempObj->getAddress(newObj->dallasTempAddr, 0)) { - // attempt to set sensor resolution - newObj->dallasTempObj->setResolution(msgDs18x20InitReq->sensor_resolution); - // copy the device's sensor properties - newObj->sensorPropertiesCount = - msgDs18x20InitReq->i2c_device_properties_count; - // TODO: Make sure this works, it's a new idea and untested :) - for (int i = 0; i < newObj->sensorPropertiesCount; i++) { - newObj->sensorProperties[i].sensor_type = - msgDs18x20InitReq->i2c_device_properties[i].sensor_type; - newObj->sensorProperties[i].sensor_period = - (long)msgDs18x20InitReq->i2c_device_properties[i].sensor_period * - 1000; - } - // set pin - strcpy(newObj->onewire_pin, msgDs18x20InitReq->onewire_pin); - // add the new ds18x20 driver to vec. - _ds18xDrivers.push_back(newObj); - is_success = true; - } else { - WS_DEBUG_PRINTLN("Failed to find DSx sensor on specified pin."); - } - - // fill and publish the initialization response back to the broker - size_t msgSz; // message's encoded size - wippersnapper_signal_v1_Ds18x20Response msgInitResp = - wippersnapper_signal_v1_Ds18x20Response_init_zero; - msgInitResp.which_payload = - wippersnapper_signal_v1_Ds18x20Response_resp_ds18x20_init_tag; - msgInitResp.payload.resp_ds18x20_init.is_initialized = is_success; - strcpy(msgInitResp.payload.resp_ds18x20_init.onewire_pin, - msgDs18x20InitReq->onewire_pin); - - WS_DEBUG_PRINT("Created OneWireBus on GPIO "); - WS_DEBUG_PRINT(msgDs18x20InitReq->onewire_pin); - WS_DEBUG_PRINTLN(" with DS18x20 attached!"); - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[DS18x] Attached DS18x20 sensor to pin %s\n", - msgDs18x20InitReq->onewire_pin); - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - // Encode and publish response back to broker - 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 (!ws_pb_encode(&ostream, wippersnapper_signal_v1_Ds18x20Response_fields, - &msgInitResp)) { - WS_DEBUG_PRINTLN("ERROR: Unable to encode msg_init response message!"); - return false; - } - pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_Ds18x20Response_fields, - &msgInitResp); - WS_DEBUG_PRINT("-> DS18x Init Response..."); - WS._mqtt->publish(WS._topic_signal_ds18_device, WS._buffer_outgoing, msgSz, - 1); - WS_DEBUG_PRINTLN("Published!"); - - return is_success; -} - -/********************************************************************/ -/*! - @brief De-initializes a DS18x20 sensor and releases its - pin and resources. - @param msgDS18x20DeinitReq - Message containing configuration data for a - ds18x20 sensor. -*/ -/********************************************************************/ -void ws_ds18x20::deleteDS18x20( - wippersnapper_ds18x20_v1_Ds18x20DeInitRequest *msgDS18x20DeinitReq) { - // Loop thru vector of drivers to find the unique address - for (size_t idx = 0; idx < _ds18xDrivers.size(); idx++) { - if (strcmp(_ds18xDrivers[idx]->onewire_pin, - msgDS18x20DeinitReq->onewire_pin) == 0) { - WS_DEBUG_PRINT("Deleting OneWire instance on pin "); - WS_DEBUG_PRINTLN(msgDS18x20DeinitReq->onewire_pin); - delete _ds18xDrivers[idx] - ->dallasTempObj; // delete dallas temp instance on pin - delete _ds18xDrivers[idx] - ->oneWire; // delete OneWire instance on pin and release pin for reuse - _ds18xDrivers.erase(_ds18xDrivers.begin() + - idx); // erase vector and re-allocate - } - } - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[DS18x] Deleted DS18x20 sensor on pin %s\n", - msgDS18x20DeinitReq->onewire_pin); - WS._ui_helper->add_text_to_terminal(buffer); -#endif -} - -/*************************************************************/ -/*! - @brief Iterates through each ds18x20 sensor and - reports data (if period expired) to Adafruit IO. -*/ -/*************************************************************/ -void ws_ds18x20::update() { - // return immediately if no drivers have been initialized - if (_ds18xDrivers.size() == 0) - return; - - long curTime; // used for holding the millis() value - std::vector::iterator iter, end; - for (iter = _ds18xDrivers.begin(), end = _ds18xDrivers.end(); iter != end; - ++iter) { - - // Create an empty DS18x20 event signal message and configure - wippersnapper_signal_v1_Ds18x20Response msgDS18x20Response = - wippersnapper_signal_v1_Ds18x20Response_init_zero; - msgDS18x20Response.which_payload = - wippersnapper_signal_v1_Ds18x20Response_resp_ds18x20_event_tag; - - // take the current time for the driver (*iter) - curTime = millis(); - // Poll each sensor type, if period has elapsed - for (int i = 0; i < (*iter)->sensorPropertiesCount; i++) { - // has sensor_period elapsed? - if (curTime - (*iter)->sensorPeriodPrv > - (long)(*iter)->sensorProperties[i].sensor_period) { - // issue global temperature request to all DS sensors - WS_DEBUG_PRINTLN("Requesting temperature.."); - (*iter)->dallasTempObj->requestTemperatures(); - // poll the DS sensor driver - float tempC = (*iter)->dallasTempObj->getTempC((*iter)->dallasTempAddr); - if (tempC == DEVICE_DISCONNECTED_C) { - WS_DEBUG_PRINTLN("ERROR: Could not read temperature data, is the " - "sensor disconnected?"); -#ifdef USE_DISPLAY - WS._ui_helper->add_text_to_terminal( - "[DS18x ERROR] Unable to read temperature, is the sensor " - "disconnected?\n"); -#endif - break; - } - - // check and pack based on sensorType - char buffer[100]; - if ((*iter)->sensorProperties[i].sensor_type == - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE) { - - WS_DEBUG_PRINT("(OneWireBus GPIO: "); - WS_DEBUG_PRINT((*iter)->onewire_pin); - WS_DEBUG_PRINT(") DS18x20 Value: "); - WS_DEBUG_PRINT(tempC); - WS_DEBUG_PRINTLN("*C") - snprintf(buffer, 100, "[DS18x] Read %0.2f*C on GPIO %s\n", tempC, - (*iter)->onewire_pin); - - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].type = - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE; - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].value = - tempC; - - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count++; - } - - if ((*iter)->sensorProperties[i].sensor_type == - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT) { - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].type = - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT; - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].value = - (*iter)->dallasTempObj->toFahrenheit(tempC); - WS_DEBUG_PRINT("(OneWireBus GPIO: "); - WS_DEBUG_PRINT((*iter)->onewire_pin); - WS_DEBUG_PRINT(") DS18x20 Value: "); - WS_DEBUG_PRINT( - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] - .value); - WS_DEBUG_PRINTLN("*F") - snprintf(buffer, 100, "[DS18x] Read %0.2f*F on GPIO %s\n", - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] - .value, - (*iter)->onewire_pin); - - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count++; - } - - // did we obtain the expected amount of sensor events for the - // `resp_ds18x20_event` message? - if (msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count == - (*iter)->sensorPropertiesCount) { - - // prep sensor event data for sending to IO - // use onewire_pin as the "address" - strcpy(msgDS18x20Response.payload.resp_ds18x20_event.onewire_pin, - (*iter)->onewire_pin); - // prep and encode buffer - 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 (!ws_pb_encode(&ostream, - wippersnapper_signal_v1_Ds18x20Response_fields, - &msgDS18x20Response)) { - WS_DEBUG_PRINTLN( - "ERROR: Unable to encode DS18x20 event responsemessage!"); - snprintf(buffer, 100, - "[DS18x ERROR] Unable to encode event message!"); - return; - } - - WS_DEBUG_PRINTLN( - "DEBUG: msgDS18x20Response sensor_event message contents:"); - for (int i = 0; - i < - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count; - i++) { - WS_DEBUG_PRINT("sensor_event[#]: "); - WS_DEBUG_PRINTLN(i); - WS_DEBUG_PRINT("\tOneWire Bus: "); - WS_DEBUG_PRINTLN( - msgDS18x20Response.payload.resp_ds18x20_event.onewire_pin); - WS_DEBUG_PRINT("\tsensor_event type: "); - WS_DEBUG_PRINTLN( - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] - .type); - WS_DEBUG_PRINT("\tsensor_event value: "); - WS_DEBUG_PRINTLN( - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] - .value); - } - - // Publish I2CResponse msg - size_t msgSz; - pb_get_encoded_size(&msgSz, - wippersnapper_signal_v1_Ds18x20Response_fields, - &msgDS18x20Response); - 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!"); -#ifdef USE_DISPLAY - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - (*iter)->sensorPeriodPrv = curTime; // set prv period - } - } - } - } +/*! + * @file ws_ds18x20.cpp + * + * This component implements 1-wire communication + * for the DS18X20-line of Maxim Temperature ICs. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2022-2023 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#include "ws_ds18x20.h" + +/*************************************************************/ +/*! + @brief Creates a new WipperSnapper Ds18x20 component. +*/ +/*************************************************************/ +ws_ds18x20::ws_ds18x20() {} + +/*************************************************************/ +/*! + @brief Destructor for a WipperSnapper DS18X20 component. +*/ +/*************************************************************/ +ws_ds18x20::~ws_ds18x20() { + // delete DallasTemp sensors and release onewire buses + for (size_t idx = 0; idx < _ds18xDrivers.size(); idx++) { + delete _ds18xDrivers[idx]->dallasTempObj; + delete _ds18xDrivers[idx]->oneWire; + } + // remove all elements + _ds18xDrivers.clear(); +} + +/********************************************************************/ +/*! + @brief Initializes a DS18x20 sensor using a + configuration sent by the broker and adds it to a + vector of ds18x20 sensor drivers. + @param msgDs18x20InitReq + Message containing configuration data for a + ds18x20 sensor. + @returns True if initialized successfully, False otherwise. +*/ +/********************************************************************/ +bool ws_ds18x20::addDS18x20( + wippersnapper_ds18x20_v1_Ds18x20InitRequest *msgDs18x20InitReq) { + bool is_success = false; + + // init. new ds18x20 object + ds18x20Obj *newObj = new ds18x20Obj(); + char *oneWirePin = msgDs18x20InitReq->onewire_pin + 1; + newObj->oneWire = new OneWire(atoi(oneWirePin)); + newObj->dallasTempObj = new DallasTemperature(newObj->oneWire); + newObj->dallasTempObj->begin(); + // attempt to obtain sensor address + if (newObj->dallasTempObj->getAddress(newObj->dallasTempAddr, 0)) { + // attempt to set sensor resolution + newObj->dallasTempObj->setResolution(msgDs18x20InitReq->sensor_resolution); + // copy the device's sensor properties + newObj->sensorPropertiesCount = + msgDs18x20InitReq->i2c_device_properties_count; + // TODO: Make sure this works, it's a new idea and untested :) + for (int i = 0; i < newObj->sensorPropertiesCount; i++) { + newObj->sensorProperties[i].sensor_type = + msgDs18x20InitReq->i2c_device_properties[i].sensor_type; + newObj->sensorProperties[i].sensor_period = + (long)msgDs18x20InitReq->i2c_device_properties[i].sensor_period * + 1000; + } + // set pin + strcpy(newObj->onewire_pin, msgDs18x20InitReq->onewire_pin); + // add the new ds18x20 driver to vec. + _ds18xDrivers.push_back(newObj); + is_success = true; + } else { + WS_DEBUG_PRINTLN("Failed to find DSx sensor on specified pin."); + } + + // fill and publish the initialization response back to the broker + size_t msgSz; // message's encoded size + wippersnapper_signal_v1_Ds18x20Response msgInitResp = + wippersnapper_signal_v1_Ds18x20Response_init_zero; + msgInitResp.which_payload = + wippersnapper_signal_v1_Ds18x20Response_resp_ds18x20_init_tag; + msgInitResp.payload.resp_ds18x20_init.is_initialized = is_success; + strcpy(msgInitResp.payload.resp_ds18x20_init.onewire_pin, + msgDs18x20InitReq->onewire_pin); + + WS_DEBUG_PRINT("Created OneWireBus on GPIO "); + WS_DEBUG_PRINT(msgDs18x20InitReq->onewire_pin); + WS_DEBUG_PRINTLN(" with DS18x20 attached!"); + +#ifdef USE_DISPLAY + char buffer[100]; + snprintf(buffer, 100, "[DS18x] Attached DS18x20 sensor to pin %s\n", + msgDs18x20InitReq->onewire_pin); + WS._ui_helper->add_text_to_terminal(buffer); +#endif + + // Encode and publish response back to broker + 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 (!ws_pb_encode(&ostream, wippersnapper_signal_v1_Ds18x20Response_fields, + &msgInitResp)) { + WS_DEBUG_PRINTLN("ERROR: Unable to encode msg_init response message!"); + return false; + } + pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_Ds18x20Response_fields, + &msgInitResp); + WS_DEBUG_PRINT("-> DS18x Init Response..."); + WS._mqtt->publish(WS._topic_signal_ds18_device, WS._buffer_outgoing, msgSz, + 1); + WS_DEBUG_PRINTLN("Published!"); + + return is_success; +} + +/********************************************************************/ +/*! + @brief De-initializes a DS18x20 sensor and releases its + pin and resources. + @param msgDS18x20DeinitReq + Message containing configuration data for a + ds18x20 sensor. +*/ +/********************************************************************/ +void ws_ds18x20::deleteDS18x20( + wippersnapper_ds18x20_v1_Ds18x20DeInitRequest *msgDS18x20DeinitReq) { + // Loop thru vector of drivers to find the unique address + for (size_t idx = 0; idx < _ds18xDrivers.size(); idx++) { + if (strcmp(_ds18xDrivers[idx]->onewire_pin, + msgDS18x20DeinitReq->onewire_pin) == 0) { + WS_DEBUG_PRINT("Deleting OneWire instance on pin "); + WS_DEBUG_PRINTLN(msgDS18x20DeinitReq->onewire_pin); + delete _ds18xDrivers[idx] + ->dallasTempObj; // delete dallas temp instance on pin + delete _ds18xDrivers[idx] + ->oneWire; // delete OneWire instance on pin and release pin for reuse + _ds18xDrivers.erase(_ds18xDrivers.begin() + + idx); // erase vector and re-allocate + } + } + +#ifdef USE_DISPLAY + char buffer[100]; + snprintf(buffer, 100, "[DS18x] Deleted DS18x20 sensor on pin %s\n", + msgDS18x20DeinitReq->onewire_pin); + WS._ui_helper->add_text_to_terminal(buffer); +#endif +} + +/*************************************************************/ +/*! + @brief Iterates through each ds18x20 sensor and + reports data (if period expired) to Adafruit IO. +*/ +/*************************************************************/ +void ws_ds18x20::update() { + // return immediately if no drivers have been initialized + if (_ds18xDrivers.size() == 0) + return; + + long curTime; // used for holding the millis() value + std::vector::iterator iter, end; + for (iter = _ds18xDrivers.begin(), end = _ds18xDrivers.end(); iter != end; + ++iter) { + + // Create an empty DS18x20 event signal message and configure + wippersnapper_signal_v1_Ds18x20Response msgDS18x20Response = + wippersnapper_signal_v1_Ds18x20Response_init_zero; + msgDS18x20Response.which_payload = + wippersnapper_signal_v1_Ds18x20Response_resp_ds18x20_event_tag; + + // take the current time for the driver (*iter) + curTime = millis(); + // Poll each sensor type, if period has elapsed + for (int i = 0; i < (*iter)->sensorPropertiesCount; i++) { + // has sensor_period elapsed? + if (curTime - (*iter)->sensorPeriodPrv > + (long)(*iter)->sensorProperties[i].sensor_period) { + // issue global temperature request to all DS sensors + WS_DEBUG_PRINTLN("Requesting temperature.."); + (*iter)->dallasTempObj->requestTemperatures(); + // poll the DS sensor driver + float tempC = (*iter)->dallasTempObj->getTempC((*iter)->dallasTempAddr); + if (tempC == DEVICE_DISCONNECTED_C) { + WS_DEBUG_PRINTLN("ERROR: Could not read temperature data, is the " + "sensor disconnected?"); +#ifdef USE_DISPLAY + WS._ui_helper->add_text_to_terminal( + "[DS18x ERROR] Unable to read temperature, is the sensor " + "disconnected?\n"); +#endif + break; + } + + // check and pack based on sensorType + char buffer[100]; + if ((*iter)->sensorProperties[i].sensor_type == + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE) { + + WS_DEBUG_PRINT("(OneWireBus GPIO: "); + WS_DEBUG_PRINT((*iter)->onewire_pin); + WS_DEBUG_PRINT(") DS18x20 Value: "); + WS_DEBUG_PRINT(tempC); + WS_DEBUG_PRINTLN("*C") + snprintf(buffer, 100, "[DS18x] Read %0.2f*C on GPIO %s\n", tempC, + (*iter)->onewire_pin); + + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].type = + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE; + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].value = + tempC; + + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count++; + } + + if ((*iter)->sensorProperties[i].sensor_type == + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT) { + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].type = + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT; + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].value = + (*iter)->dallasTempObj->toFahrenheit(tempC); + WS_DEBUG_PRINT("(OneWireBus GPIO: "); + WS_DEBUG_PRINT((*iter)->onewire_pin); + WS_DEBUG_PRINT(") DS18x20 Value: "); + WS_DEBUG_PRINT( + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] + .value); + WS_DEBUG_PRINTLN("*F") + snprintf(buffer, 100, "[DS18x] Read %0.2f*F on GPIO %s\n", + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] + .value, + (*iter)->onewire_pin); + + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count++; + } + + // did we obtain the expected amount of sensor events for the + // `resp_ds18x20_event` message? + if (msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count == + (*iter)->sensorPropertiesCount) { + + // prep sensor event data for sending to IO + // use onewire_pin as the "address" + strcpy(msgDS18x20Response.payload.resp_ds18x20_event.onewire_pin, + (*iter)->onewire_pin); + // prep and encode buffer + 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 (!ws_pb_encode(&ostream, + wippersnapper_signal_v1_Ds18x20Response_fields, + &msgDS18x20Response)) { + WS_DEBUG_PRINTLN( + "ERROR: Unable to encode DS18x20 event responsemessage!"); + snprintf(buffer, 100, + "[DS18x ERROR] Unable to encode event message!"); + return; + } + + WS_DEBUG_PRINTLN( + "DEBUG: msgDS18x20Response sensor_event message contents:"); + for (int i = 0; + i < + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count; + i++) { + WS_DEBUG_PRINT("sensor_event[#]: "); + WS_DEBUG_PRINTLN(i); + WS_DEBUG_PRINT("\tOneWire Bus: "); + WS_DEBUG_PRINTLN( + msgDS18x20Response.payload.resp_ds18x20_event.onewire_pin); + WS_DEBUG_PRINT("\tsensor_event type: "); + WS_DEBUG_PRINTLN( + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] + .type); + WS_DEBUG_PRINT("\tsensor_event value: "); + WS_DEBUG_PRINTLN( + msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] + .value); + } + + // Publish I2CResponse msg + size_t msgSz; + pb_get_encoded_size(&msgSz, + wippersnapper_signal_v1_Ds18x20Response_fields, + &msgDS18x20Response); + 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!"); +#ifdef USE_DISPLAY + WS._ui_helper->add_text_to_terminal(buffer); +#endif + + (*iter)->sensorPeriodPrv = curTime; // set prv period + } + } + } + } } \ No newline at end of file diff --git a/src/components/pixels/ws_pixels.cpp b/src/components/pixels/ws_pixels.cpp index c77149ba1..a35eb6e42 100644 --- a/src/components/pixels/ws_pixels.cpp +++ b/src/components/pixels/ws_pixels.cpp @@ -1,453 +1,453 @@ -/*! - * @file ws_pixels.cpp - * - * High-level interface for wippersnapper to manage addressable RGB pixel - * strands - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * - * Written by Brent Rubell for Adafruit Industries, 2022-2023 - * - * MIT license, all text here must be included in any redistribution. - * - */ -#include "ws_pixels.h" - -strand_s strands[MAX_PIXEL_STRANDS]{ - nullptr, - nullptr, - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_UNSPECIFIED, - 0, - 0, - wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_UNSPECIFIED, - -1, - -1, - -1}; ///< Contains all pixel strands used by WipperSnapper - -/**************************************************************************/ -/*! - @brief Destructor -*/ -/**************************************************************************/ -ws_pixels::~ws_pixels() { - // de-allocate all strands - for (size_t i = 0; i < sizeof(strands) / sizeof(strands[0]); i++) - deallocateStrand(i); -} - -/******************************************************************************/ -/*! - @brief Allocates an index of a free strand_t within the strand array. - @returns Index of a free strand_t, ERR_INVALID_STRAND if strand array is - full. -*/ -/******************************************************************************/ -int16_t ws_pixels::allocateStrand() { - for (size_t strandIdx = 0; strandIdx < sizeof(strands) / sizeof(strands[0]); - strandIdx++) { - if (strands[strandIdx].type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_UNSPECIFIED) { - return strandIdx; - } - } - return ERR_INVALID_STRAND; -} - -/**************************************************************************/ -/*! - @brief Deallocates a `strand_t` within `strands`, provided an index. - @param strandIdx - The desired index of a `strand_t` within `strands`. -*/ -/**************************************************************************/ -void ws_pixels::deallocateStrand(int16_t strandIdx) { - - // delete the pixel object - if (strands[strandIdx].neoPixelPtr != nullptr) - delete strands[strandIdx].neoPixelPtr; - if ((strands[strandIdx].dotStarPtr != nullptr)) - delete strands[strandIdx].dotStarPtr; - - // re-initialize status pixel (if pixel was prvsly used) - if (strands[strandIdx].pinNeoPixel == getStatusNeoPixelPin() || - strands[strandIdx].pinDotStarData == getStatusDotStarDataPin()) { - initStatusLED(); - } - - // reset the strand - strands[strandIdx] = { - nullptr, - nullptr, - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_UNSPECIFIED, - 0, - 0, - wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_UNSPECIFIED, - -1, - -1, - -1}; -} - -/**************************************************************************/ -/*! - @brief Returns the `neoPixelType` provided the strand's pixelOrder - @param pixelOrder - Desired pixel order, from init. message. - @returns Type of NeoPixel strand, usable by Adafruit_NeoPixel - constructor -*/ -/**************************************************************************/ -neoPixelType ws_pixels::getNeoPixelStrandOrder( - wippersnapper_pixels_v1_PixelsOrder pixelOrder) { - switch (pixelOrder) { - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GRB: - return NEO_GRB + NEO_KHZ800; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GRBW: - return NEO_GRBW + NEO_KHZ800; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RGB: - return NEO_RGB + NEO_KHZ800; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RGBW: - return NEO_RGBW + NEO_KHZ800; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_BRG: - return NEO_BRG + NEO_KHZ800; - default: - return NEO_GRB + NEO_KHZ800; - } -} - -/**************************************************************************/ -/*! - @brief Returns the color order of the DotStar strand. - @param pixelOrder - Desired pixel order, from init. message. - @returns Type of DotStar strand. -*/ -/**************************************************************************/ -uint8_t ws_pixels::getDotStarStrandOrder( - wippersnapper_pixels_v1_PixelsOrder pixelOrder) { - switch (pixelOrder) { - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GRB: - return DOTSTAR_GRB; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RGB: - return DOTSTAR_RGB; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_BRG: - return DOTSTAR_BRG; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RBG: - return DOTSTAR_RBG; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GBR: - return DOTSTAR_GBR; - case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_BGR: - return DOTSTAR_BGR; - default: - return DOTSTAR_BRG; - } -} - -/**************************************************************************/ -/*! - @brief Creates a PixelsResponse message and publishes it to IO. - @param is_success - True if `addStrand()` succeeded, False otherwise. - @param pixels_pin_data - The strand's data pin.. -*/ -/**************************************************************************/ -void ws_pixels::publishAddStrandResponse(bool is_success, - char *pixels_pin_data) { - // Create response message - wippersnapper_signal_v1_PixelsResponse msgInitResp = - wippersnapper_signal_v1_PixelsResponse_init_zero; - msgInitResp.which_payload = - wippersnapper_signal_v1_PixelsResponse_resp_pixels_create_tag; - // Fill response message - msgInitResp.payload.resp_pixels_create.is_success = is_success; - memcpy(msgInitResp.payload.resp_pixels_create.pixels_pin_data, - pixels_pin_data, sizeof(char) * 6); - - // Encode `wippersnapper_pixels_v1_PixelsCreateResponse` message - 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 (!ws_pb_encode(&ostream, wippersnapper_signal_v1_PixelsResponse_fields, - &msgInitResp)) { - WS_DEBUG_PRINTLN("ERROR: Unable to encode " - "wippersnapper_signal_v1_PixelsResponse message!"); - return; - } - - // Publish message to broker - size_t msgSz; - pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_PixelsResponse_fields, - &msgInitResp); - WS_DEBUG_PRINT("-> wippersnapper_signal_v1_PixelsResponse..."); - WS._mqtt->publish(WS._topic_signal_pixels_device, WS._buffer_outgoing, msgSz, - 1); - WS_DEBUG_PRINTLN("Published!"); -} - -/**************************************************************************/ -/*! - @brief Initializes a strand of addressable RGB Pixels. - @param pixelsCreateReqMsg - Pointer to strand init. request message - @returns True if successfully initialized, False otherwise. -*/ -/**************************************************************************/ -bool ws_pixels::addStrand( - wippersnapper_pixels_v1_PixelsCreateRequest *pixelsCreateReqMsg) { - // attempt to allocate a free strand from array of strands - int16_t strandIdx = allocateStrand(); - if (strandIdx == ERR_INVALID_STRAND) { // unable to allocate a strand - if (pixelsCreateReqMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) - publishAddStrandResponse(false, pixelsCreateReqMsg->pixels_pin_neopixel); - else if (pixelsCreateReqMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) - publishAddStrandResponse(false, - pixelsCreateReqMsg->pixels_pin_dotstar_data); - return false; - } - - // fill generic members of the strand obj. - strands[strandIdx].type = pixelsCreateReqMsg->pixels_type; - strands[strandIdx].brightness = pixelsCreateReqMsg->pixels_brightness; - strands[strandIdx].numPixels = pixelsCreateReqMsg->pixels_num; - strands[strandIdx].ordering = pixelsCreateReqMsg->pixels_ordering; - - // fill strand pins - if (pixelsCreateReqMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) { - strands[strandIdx].pinNeoPixel = - atoi(pixelsCreateReqMsg->pixels_pin_neopixel + 1); - } else if (pixelsCreateReqMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) { - strands[strandIdx].pinDotStarData = - atoi(pixelsCreateReqMsg->pixels_pin_dotstar_data + 1); - strands[strandIdx].pinDotStarClock = - atoi(pixelsCreateReqMsg->pixels_pin_dotstar_clock + 1); - } else { - WS_DEBUG_PRINTLN("ERROR: Invalid strand type provided!"); - publishAddStrandResponse(false, pixelsCreateReqMsg->pixels_pin_neopixel); - return false; - } - - // Fill specific members of strand obj. - if (pixelsCreateReqMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) { - // Release status LED - // is requested pin in-use by the status pixel? - if (getStatusNeoPixelPin() == strands[strandIdx].pinNeoPixel && - WS.lockStatusNeoPixel) - releaseStatusLED(); // release it! - - // Create a new strand of NeoPixels - strands[strandIdx].neoPixelPtr = new Adafruit_NeoPixel( - pixelsCreateReqMsg->pixels_num, strands[strandIdx].pinNeoPixel, - getNeoPixelStrandOrder(pixelsCreateReqMsg->pixels_ordering)); - - // Initialize strand - strands[strandIdx].neoPixelPtr->begin(); - strands[strandIdx].neoPixelPtr->setBrightness( - strands[strandIdx].brightness); - strands[strandIdx].neoPixelPtr->clear(); - strands[strandIdx].neoPixelPtr->show(); - - // Check that we've correctly initialized the strand - if (strands[strandIdx].neoPixelPtr->numPixels() == 0) { - publishAddStrandResponse(false, pixelsCreateReqMsg->pixels_pin_neopixel); - return false; - } - - WS_DEBUG_PRINT("Created NeoPixel strand of length "); - WS_DEBUG_PRINT(pixelsCreateReqMsg->pixels_num); - WS_DEBUG_PRINT(" on GPIO #"); - WS_DEBUG_PRINTLN(pixelsCreateReqMsg->pixels_pin_neopixel); - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[Pixel] Added NeoPixel strand on Pin %s\n.", - pixelsCreateReqMsg->pixels_pin_neopixel); - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - publishAddStrandResponse(true, pixelsCreateReqMsg->pixels_pin_neopixel); - } else if (pixelsCreateReqMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) { - - // release the status dotstar, if it is both in-use and the pin within - // `pixelsCreateReqMsg` - if ((strands[strandIdx].pinDotStarData == getStatusDotStarDataPin()) && - WS.lockStatusDotStar) { - releaseStatusLED(); - } - - // Create Dotstar strand - strands[strandIdx].dotStarPtr = new Adafruit_DotStar( - strands[strandIdx].numPixels, strands[strandIdx].pinDotStarData, - strands[strandIdx].pinDotStarClock, - getDotStarStrandOrder(strands[strandIdx].ordering)); - - // initialize strand - strands[strandIdx].dotStarPtr->begin(); - strands[strandIdx].dotStarPtr->setBrightness(strands[strandIdx].brightness); - strands[strandIdx].dotStarPtr->clear(); - strands[strandIdx].dotStarPtr->show(); - - // post-init sanity check - if (strands[strandIdx].dotStarPtr->numPixels() == 0) { - publishAddStrandResponse(false, - pixelsCreateReqMsg->pixels_pin_dotstar_data); - return false; - } - - WS_DEBUG_PRINT("Created DotStar strand of length "); - WS_DEBUG_PRINT(strands[strandIdx].numPixels); - WS_DEBUG_PRINT(" on Data GPIO #"); - WS_DEBUG_PRINTLN(strands[strandIdx].pinDotStarData); - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[Pixel] Added NeoPixel strand on Pin %s\n.", - pixelsCreateReqMsg->pixels_pin_neopixel); - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - publishAddStrandResponse(true, pixelsCreateReqMsg->pixels_pin_dotstar_data); - } else { - WS_DEBUG_PRINTLN("ERROR: Invalid strand type provided!"); - publishAddStrandResponse(false, - pixelsCreateReqMsg->pixels_pin_dotstar_data); - return false; - } - - return true; -} - -/**************************************************************************/ -/*! - @brief Obtains the index of a `strand_t` within array of `strands`. - @param dataPin - strand_t's data dataPin - @param type - Type of strand_t, NeoPixel or DotStar. - @returns The index of a strand_t if within strands[], - ERR_INVALID_STRAND otherwise. -*/ -/**************************************************************************/ -int ws_pixels::getStrandIdx(int16_t dataPin, - wippersnapper_pixels_v1_PixelsType type) { - for (size_t strandIdx = 0; strandIdx < sizeof(strands) / sizeof(strands[0]); - strandIdx++) { - if (type == wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL && - strands[strandIdx].pinNeoPixel == dataPin) - return strandIdx; - if (type == wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR && - strands[strandIdx].pinDotStarData == dataPin) - return strandIdx; - } - return ERR_INVALID_STRAND; -} - -/**************************************************************************/ -/*! - @brief Deletes a `strand_t` from `strands`, deinitializes a strand, - and frees its resources. - @param pixelsDeleteMsg - Protobuf message from Adafruit IO containing a - `wippersnapper_pixels_v1_PixelsDeleteRequest`. -*/ -/**************************************************************************/ -void ws_pixels::deleteStrand( - wippersnapper_pixels_v1_PixelsDeleteRequest *pixelsDeleteMsg) { - int strandIdx = getStrandIdx(atoi(pixelsDeleteMsg->pixels_pin_data + 1), - pixelsDeleteMsg->pixels_type); - if (strandIdx == ERR_INVALID_STRAND) { - WS_DEBUG_PRINTLN("ERROR: Strand not found, unable to delete strand!"); - return; - } - // deallocate and release resources of strand object - deallocateStrand(strandIdx); - - WS_DEBUG_PRINT("Deleted strand on data pin "); - WS_DEBUG_PRINTLN(pixelsDeleteMsg->pixels_pin_data); - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[Pixel] Deleted strand on pin %s\n.", - pixelsDeleteMsg->pixels_pin_data); - WS._ui_helper->add_text_to_terminal(buffer); -#endif -} - -/**************************************************************************/ -/*! - @brief Gets the gamma-corrected color, provided a pixel_color - @param pixel_color - Strand's color from Adafruit IO. - @param strand - Desired strand struct. to access. - @returns A gamma-corrected strand color -*/ -/**************************************************************************/ -uint32_t ws_pixels::getGammaCorrectedColor(uint32_t pixel_color, - strand_s strand) { - if (strand.neoPixelPtr != nullptr) { - return strand.neoPixelPtr->gamma32(pixel_color); - } else if (strand.dotStarPtr != nullptr) { - return strand.dotStarPtr->gamma32(pixel_color); - } else { - WS_DEBUG_PRINTLN( - "ERROR: Unable to perform gamma correction, unknown strand type!"); - return 0; - } -} - -/**************************************************************************/ -/*! - @brief Writes a color from Adafruit IO to a strand of - addressable pixels - @param pixelsWriteMsg - Protobuf message from Adafruit IO containing a - `wippersnapper_pixels_v1_PixelsWriteRequest`. -*/ -/**************************************************************************/ -void ws_pixels::fillStrand( - wippersnapper_pixels_v1_PixelsWriteRequest *pixelsWriteMsg) { - - // Get index of pixel strand - int strandIdx = getStrandIdx(atoi(pixelsWriteMsg->pixels_pin_data + 1), - pixelsWriteMsg->pixels_type); - if (strandIdx == ERR_INVALID_STRAND) { - WS_DEBUG_PRINTLN( - "ERROR: Pixel strand not found, can not write a color to the strand!"); - return; - } - - uint32_t rgbColorGamma = - getGammaCorrectedColor(pixelsWriteMsg->pixels_color, strands[strandIdx]); - - WS_DEBUG_PRINT("Filling color: "); - WS_DEBUG_PRINTLN(pixelsWriteMsg->pixels_color); - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[Pixel] Filling strand on pin %s with color %u\n", - pixelsWriteMsg->pixels_pin_data, - (unsigned int)pixelsWriteMsg->pixels_color); - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - if (pixelsWriteMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) { - strands[strandIdx].neoPixelPtr->fill(rgbColorGamma); - strands[strandIdx].neoPixelPtr->show(); - } else if (pixelsWriteMsg->pixels_type == - wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) { - strands[strandIdx].dotStarPtr->fill(rgbColorGamma); - strands[strandIdx].dotStarPtr->show(); - } else { - WS_DEBUG_PRINTLN("ERROR: Unable to determine pixel type to write to!"); - } +/*! + * @file ws_pixels.cpp + * + * High-level interface for wippersnapper to manage addressable RGB pixel + * strands + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * + * Written by Brent Rubell for Adafruit Industries, 2022-2023 + * + * MIT license, all text here must be included in any redistribution. + * + */ +#include "ws_pixels.h" + +strand_s strands[MAX_PIXEL_STRANDS]{ + nullptr, + nullptr, + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_UNSPECIFIED, + 0, + 0, + wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_UNSPECIFIED, + -1, + -1, + -1}; ///< Contains all pixel strands used by WipperSnapper + +/**************************************************************************/ +/*! + @brief Destructor +*/ +/**************************************************************************/ +ws_pixels::~ws_pixels() { + // de-allocate all strands + for (size_t i = 0; i < sizeof(strands) / sizeof(strands[0]); i++) + deallocateStrand(i); +} + +/******************************************************************************/ +/*! + @brief Allocates an index of a free strand_t within the strand array. + @returns Index of a free strand_t, ERR_INVALID_STRAND if strand array is + full. +*/ +/******************************************************************************/ +int16_t ws_pixels::allocateStrand() { + for (size_t strandIdx = 0; strandIdx < sizeof(strands) / sizeof(strands[0]); + strandIdx++) { + if (strands[strandIdx].type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_UNSPECIFIED) { + return strandIdx; + } + } + return ERR_INVALID_STRAND; +} + +/**************************************************************************/ +/*! + @brief Deallocates a `strand_t` within `strands`, provided an index. + @param strandIdx + The desired index of a `strand_t` within `strands`. +*/ +/**************************************************************************/ +void ws_pixels::deallocateStrand(int16_t strandIdx) { + + // delete the pixel object + if (strands[strandIdx].neoPixelPtr != nullptr) + delete strands[strandIdx].neoPixelPtr; + if ((strands[strandIdx].dotStarPtr != nullptr)) + delete strands[strandIdx].dotStarPtr; + + // re-initialize status pixel (if pixel was prvsly used) + if (strands[strandIdx].pinNeoPixel == getStatusNeoPixelPin() || + strands[strandIdx].pinDotStarData == getStatusDotStarDataPin()) { + initStatusLED(); + } + + // reset the strand + strands[strandIdx] = { + nullptr, + nullptr, + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_UNSPECIFIED, + 0, + 0, + wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_UNSPECIFIED, + -1, + -1, + -1}; +} + +/**************************************************************************/ +/*! + @brief Returns the `neoPixelType` provided the strand's pixelOrder + @param pixelOrder + Desired pixel order, from init. message. + @returns Type of NeoPixel strand, usable by Adafruit_NeoPixel + constructor +*/ +/**************************************************************************/ +neoPixelType ws_pixels::getNeoPixelStrandOrder( + wippersnapper_pixels_v1_PixelsOrder pixelOrder) { + switch (pixelOrder) { + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GRB: + return NEO_GRB + NEO_KHZ800; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GRBW: + return NEO_GRBW + NEO_KHZ800; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RGB: + return NEO_RGB + NEO_KHZ800; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RGBW: + return NEO_RGBW + NEO_KHZ800; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_BRG: + return NEO_BRG + NEO_KHZ800; + default: + return NEO_GRB + NEO_KHZ800; + } +} + +/**************************************************************************/ +/*! + @brief Returns the color order of the DotStar strand. + @param pixelOrder + Desired pixel order, from init. message. + @returns Type of DotStar strand. +*/ +/**************************************************************************/ +uint8_t ws_pixels::getDotStarStrandOrder( + wippersnapper_pixels_v1_PixelsOrder pixelOrder) { + switch (pixelOrder) { + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GRB: + return DOTSTAR_GRB; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RGB: + return DOTSTAR_RGB; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_BRG: + return DOTSTAR_BRG; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_RBG: + return DOTSTAR_RBG; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_GBR: + return DOTSTAR_GBR; + case wippersnapper_pixels_v1_PixelsOrder_PIXELS_ORDER_BGR: + return DOTSTAR_BGR; + default: + return DOTSTAR_BRG; + } +} + +/**************************************************************************/ +/*! + @brief Creates a PixelsResponse message and publishes it to IO. + @param is_success + True if `addStrand()` succeeded, False otherwise. + @param pixels_pin_data + The strand's data pin.. +*/ +/**************************************************************************/ +void ws_pixels::publishAddStrandResponse(bool is_success, + char *pixels_pin_data) { + // Create response message + wippersnapper_signal_v1_PixelsResponse msgInitResp = + wippersnapper_signal_v1_PixelsResponse_init_zero; + msgInitResp.which_payload = + wippersnapper_signal_v1_PixelsResponse_resp_pixels_create_tag; + // Fill response message + msgInitResp.payload.resp_pixels_create.is_success = is_success; + memcpy(msgInitResp.payload.resp_pixels_create.pixels_pin_data, + pixels_pin_data, sizeof(char) * 6); + + // Encode `wippersnapper_pixels_v1_PixelsCreateResponse` message + 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 (!ws_pb_encode(&ostream, wippersnapper_signal_v1_PixelsResponse_fields, + &msgInitResp)) { + WS_DEBUG_PRINTLN("ERROR: Unable to encode " + "wippersnapper_signal_v1_PixelsResponse message!"); + return; + } + + // Publish message to broker + size_t msgSz; + pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_PixelsResponse_fields, + &msgInitResp); + WS_DEBUG_PRINT("-> wippersnapper_signal_v1_PixelsResponse..."); + WS._mqtt->publish(WS._topic_signal_pixels_device, WS._buffer_outgoing, msgSz, + 1); + WS_DEBUG_PRINTLN("Published!"); +} + +/**************************************************************************/ +/*! + @brief Initializes a strand of addressable RGB Pixels. + @param pixelsCreateReqMsg + Pointer to strand init. request message + @returns True if successfully initialized, False otherwise. +*/ +/**************************************************************************/ +bool ws_pixels::addStrand( + wippersnapper_pixels_v1_PixelsCreateRequest *pixelsCreateReqMsg) { + // attempt to allocate a free strand from array of strands + int16_t strandIdx = allocateStrand(); + if (strandIdx == ERR_INVALID_STRAND) { // unable to allocate a strand + if (pixelsCreateReqMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) + publishAddStrandResponse(false, pixelsCreateReqMsg->pixels_pin_neopixel); + else if (pixelsCreateReqMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) + publishAddStrandResponse(false, + pixelsCreateReqMsg->pixels_pin_dotstar_data); + return false; + } + + // fill generic members of the strand obj. + strands[strandIdx].type = pixelsCreateReqMsg->pixels_type; + strands[strandIdx].brightness = pixelsCreateReqMsg->pixels_brightness; + strands[strandIdx].numPixels = pixelsCreateReqMsg->pixels_num; + strands[strandIdx].ordering = pixelsCreateReqMsg->pixels_ordering; + + // fill strand pins + if (pixelsCreateReqMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) { + strands[strandIdx].pinNeoPixel = + atoi(pixelsCreateReqMsg->pixels_pin_neopixel + 1); + } else if (pixelsCreateReqMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) { + strands[strandIdx].pinDotStarData = + atoi(pixelsCreateReqMsg->pixels_pin_dotstar_data + 1); + strands[strandIdx].pinDotStarClock = + atoi(pixelsCreateReqMsg->pixels_pin_dotstar_clock + 1); + } else { + WS_DEBUG_PRINTLN("ERROR: Invalid strand type provided!"); + publishAddStrandResponse(false, pixelsCreateReqMsg->pixels_pin_neopixel); + return false; + } + + // Fill specific members of strand obj. + if (pixelsCreateReqMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) { + // Release status LED + // is requested pin in-use by the status pixel? + if (getStatusNeoPixelPin() == strands[strandIdx].pinNeoPixel && + WS.lockStatusNeoPixel) + releaseStatusLED(); // release it! + + // Create a new strand of NeoPixels + strands[strandIdx].neoPixelPtr = new Adafruit_NeoPixel( + pixelsCreateReqMsg->pixels_num, strands[strandIdx].pinNeoPixel, + getNeoPixelStrandOrder(pixelsCreateReqMsg->pixels_ordering)); + + // Initialize strand + strands[strandIdx].neoPixelPtr->begin(); + strands[strandIdx].neoPixelPtr->setBrightness( + strands[strandIdx].brightness); + strands[strandIdx].neoPixelPtr->clear(); + strands[strandIdx].neoPixelPtr->show(); + + // Check that we've correctly initialized the strand + if (strands[strandIdx].neoPixelPtr->numPixels() == 0) { + publishAddStrandResponse(false, pixelsCreateReqMsg->pixels_pin_neopixel); + return false; + } + + WS_DEBUG_PRINT("Created NeoPixel strand of length "); + WS_DEBUG_PRINT(pixelsCreateReqMsg->pixels_num); + WS_DEBUG_PRINT(" on GPIO #"); + WS_DEBUG_PRINTLN(pixelsCreateReqMsg->pixels_pin_neopixel); + +#ifdef USE_DISPLAY + char buffer[100]; + snprintf(buffer, 100, "[Pixel] Added NeoPixel strand on Pin %s\n.", + pixelsCreateReqMsg->pixels_pin_neopixel); + WS._ui_helper->add_text_to_terminal(buffer); +#endif + + publishAddStrandResponse(true, pixelsCreateReqMsg->pixels_pin_neopixel); + } else if (pixelsCreateReqMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) { + + // release the status dotstar, if it is both in-use and the pin within + // `pixelsCreateReqMsg` + if ((strands[strandIdx].pinDotStarData == getStatusDotStarDataPin()) && + WS.lockStatusDotStar) { + releaseStatusLED(); + } + + // Create Dotstar strand + strands[strandIdx].dotStarPtr = new Adafruit_DotStar( + strands[strandIdx].numPixels, strands[strandIdx].pinDotStarData, + strands[strandIdx].pinDotStarClock, + getDotStarStrandOrder(strands[strandIdx].ordering)); + + // initialize strand + strands[strandIdx].dotStarPtr->begin(); + strands[strandIdx].dotStarPtr->setBrightness(strands[strandIdx].brightness); + strands[strandIdx].dotStarPtr->clear(); + strands[strandIdx].dotStarPtr->show(); + + // post-init sanity check + if (strands[strandIdx].dotStarPtr->numPixels() == 0) { + publishAddStrandResponse(false, + pixelsCreateReqMsg->pixels_pin_dotstar_data); + return false; + } + + WS_DEBUG_PRINT("Created DotStar strand of length "); + WS_DEBUG_PRINT(strands[strandIdx].numPixels); + WS_DEBUG_PRINT(" on Data GPIO #"); + WS_DEBUG_PRINTLN(strands[strandIdx].pinDotStarData); + +#ifdef USE_DISPLAY + char buffer[100]; + snprintf(buffer, 100, "[Pixel] Added NeoPixel strand on Pin %s\n.", + pixelsCreateReqMsg->pixels_pin_neopixel); + WS._ui_helper->add_text_to_terminal(buffer); +#endif + + publishAddStrandResponse(true, pixelsCreateReqMsg->pixels_pin_dotstar_data); + } else { + WS_DEBUG_PRINTLN("ERROR: Invalid strand type provided!"); + publishAddStrandResponse(false, + pixelsCreateReqMsg->pixels_pin_dotstar_data); + return false; + } + + return true; +} + +/**************************************************************************/ +/*! + @brief Obtains the index of a `strand_t` within array of `strands`. + @param dataPin + strand_t's data dataPin + @param type + Type of strand_t, NeoPixel or DotStar. + @returns The index of a strand_t if within strands[], + ERR_INVALID_STRAND otherwise. +*/ +/**************************************************************************/ +int ws_pixels::getStrandIdx(int16_t dataPin, + wippersnapper_pixels_v1_PixelsType type) { + for (size_t strandIdx = 0; strandIdx < sizeof(strands) / sizeof(strands[0]); + strandIdx++) { + if (type == wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL && + strands[strandIdx].pinNeoPixel == dataPin) + return strandIdx; + if (type == wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR && + strands[strandIdx].pinDotStarData == dataPin) + return strandIdx; + } + return ERR_INVALID_STRAND; +} + +/**************************************************************************/ +/*! + @brief Deletes a `strand_t` from `strands`, deinitializes a strand, + and frees its resources. + @param pixelsDeleteMsg + Protobuf message from Adafruit IO containing a + `wippersnapper_pixels_v1_PixelsDeleteRequest`. +*/ +/**************************************************************************/ +void ws_pixels::deleteStrand( + wippersnapper_pixels_v1_PixelsDeleteRequest *pixelsDeleteMsg) { + int strandIdx = getStrandIdx(atoi(pixelsDeleteMsg->pixels_pin_data + 1), + pixelsDeleteMsg->pixels_type); + if (strandIdx == ERR_INVALID_STRAND) { + WS_DEBUG_PRINTLN("ERROR: Strand not found, unable to delete strand!"); + return; + } + // deallocate and release resources of strand object + deallocateStrand(strandIdx); + + WS_DEBUG_PRINT("Deleted strand on data pin "); + WS_DEBUG_PRINTLN(pixelsDeleteMsg->pixels_pin_data); + +#ifdef USE_DISPLAY + char buffer[100]; + snprintf(buffer, 100, "[Pixel] Deleted strand on pin %s\n.", + pixelsDeleteMsg->pixels_pin_data); + WS._ui_helper->add_text_to_terminal(buffer); +#endif +} + +/**************************************************************************/ +/*! + @brief Gets the gamma-corrected color, provided a pixel_color + @param pixel_color + Strand's color from Adafruit IO. + @param strand + Desired strand struct. to access. + @returns A gamma-corrected strand color +*/ +/**************************************************************************/ +uint32_t ws_pixels::getGammaCorrectedColor(uint32_t pixel_color, + strand_s strand) { + if (strand.neoPixelPtr != nullptr) { + return strand.neoPixelPtr->gamma32(pixel_color); + } else if (strand.dotStarPtr != nullptr) { + return strand.dotStarPtr->gamma32(pixel_color); + } else { + WS_DEBUG_PRINTLN( + "ERROR: Unable to perform gamma correction, unknown strand type!"); + return 0; + } +} + +/**************************************************************************/ +/*! + @brief Writes a color from Adafruit IO to a strand of + addressable pixels + @param pixelsWriteMsg + Protobuf message from Adafruit IO containing a + `wippersnapper_pixels_v1_PixelsWriteRequest`. +*/ +/**************************************************************************/ +void ws_pixels::fillStrand( + wippersnapper_pixels_v1_PixelsWriteRequest *pixelsWriteMsg) { + + // Get index of pixel strand + int strandIdx = getStrandIdx(atoi(pixelsWriteMsg->pixels_pin_data + 1), + pixelsWriteMsg->pixels_type); + if (strandIdx == ERR_INVALID_STRAND) { + WS_DEBUG_PRINTLN( + "ERROR: Pixel strand not found, can not write a color to the strand!"); + return; + } + + uint32_t rgbColorGamma = + getGammaCorrectedColor(pixelsWriteMsg->pixels_color, strands[strandIdx]); + + WS_DEBUG_PRINT("Filling color: "); + WS_DEBUG_PRINTLN(pixelsWriteMsg->pixels_color); + +#ifdef USE_DISPLAY + char buffer[100]; + snprintf(buffer, 100, "[Pixel] Filling strand on pin %s with color %u\n", + pixelsWriteMsg->pixels_pin_data, + (unsigned int)pixelsWriteMsg->pixels_color); + WS._ui_helper->add_text_to_terminal(buffer); +#endif + + if (pixelsWriteMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_NEOPIXEL) { + strands[strandIdx].neoPixelPtr->fill(rgbColorGamma); + strands[strandIdx].neoPixelPtr->show(); + } else if (pixelsWriteMsg->pixels_type == + wippersnapper_pixels_v1_PixelsType_PIXELS_TYPE_DOTSTAR) { + strands[strandIdx].dotStarPtr->fill(rgbColorGamma); + strands[strandIdx].dotStarPtr->show(); + } else { + WS_DEBUG_PRINTLN("ERROR: Unable to determine pixel type to write to!"); + } } \ No newline at end of file diff --git a/src/components/uart/drivers/ws_uart_drv_pm25aqi.h b/src/components/uart/drivers/ws_uart_drv_pm25aqi.h index 543761601..4eb4c0c92 100644 --- a/src/components/uart/drivers/ws_uart_drv_pm25aqi.h +++ b/src/components/uart/drivers/ws_uart_drv_pm25aqi.h @@ -1,215 +1,215 @@ -/*! - * @file ws_uart_drv_pm25aqi.h - * - * WipperSnapper device driver for the Adafruit_PM25AQI Arduino Library - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2023 for Adafruit Industries. - * - * MIT license, all text here must be included in any redistribution. - * - */ -#ifndef WS_UART_DRV_PM25AQI_H -#define WS_UART_DRV_PM25AQI_H - -#include "Wippersnapper.h" -#include "ws_uart_drv.h" -#include - -/**************************************************************************/ -/*! - @brief Class that provides an interface for a PM25 AQI UART sensor. -*/ -/**************************************************************************/ -class ws_uart_drv_pm25aqi : public ws_uart_drv { -public: -#ifdef USE_SW_UART - /*******************************************************************************/ - /*! - @brief Initializes a PM25AQI UART device driver. - @param swSerial - Pointer to an instance of a SoftwareSerial object. - @param pollingInterval - How often the PM25AQI device will be polled, in milliseconds. - */ - /*******************************************************************************/ - ws_uart_drv_pm25aqi(SoftwareSerial *swSerial, int32_t interval) - : ws_uart_drv(swSerial, interval) { - _swSerial = swSerial; - pollingInterval = (unsigned long)interval; - }; -#else - /*******************************************************************************/ - /*! - @brief Initializes the PM25AQI UART device driver. - @param hwSerial - Pointer to an instance of a HardwareSerial object. - @param interval - How often the PM25AQI device will be polled, in milliseconds. - */ - /*******************************************************************************/ - ws_uart_drv_pm25aqi(HardwareSerial *hwSerial, int32_t interval) - : ws_uart_drv(hwSerial, interval) { - _hwSerial = hwSerial; - pollingInterval = (unsigned long)interval; - }; -#endif // USE_SW_UART - - /*******************************************************************************/ - /*! - @brief Destructor for a PM25AQI sensor. - */ - /*******************************************************************************/ - ~ws_uart_drv_pm25aqi() { - delete _aqi; -#ifdef USE_SW_UART - _swSerial = nullptr; -#else - _hwSerial = nullptr; -#endif - } - - /*******************************************************************************/ - /*! - @brief Initializes a PM25AQI sensor. - @returns True if the PM25AQI sensor was successfully initialized, - False otherwise. - */ - /*******************************************************************************/ - bool begin() override { - _aqi = new Adafruit_PM25AQI(); -#ifdef USE_SW_UART - if (!_aqi->begin_UART( - _swSerial)) { // connect to the sensor over software serial - return false; - } -#else - if (!_aqi->begin_UART( - _hwSerial)) { // connect to the sensor over hardware serial - return false; - } -#endif - return true; - } - - /*******************************************************************************/ - /*! - @brief Attempts to read data from the PM25AQI sensor. - @returns True if data was successfully read, False otherwise. - */ - /*******************************************************************************/ - bool read_data() override { - Serial.println("[UART, PM25] Reading data..."); - // Attempt to read the PM2.5 Sensor - if (!_aqi->read(&_data)) { - Serial.println("[UART, PM25] Data not available."); - delay(500); - return false; - } - Serial.println("[UART, PM25] Read data OK"); - Serial.println(); - Serial.println(F("---------------------------------------")); - Serial.println(F("Concentration Units (standard)")); - Serial.println(F("---------------------------------------")); - Serial.print(F("PM 1.0: ")); - Serial.print(_data.pm10_standard); - Serial.print(F("\t\tPM 2.5: ")); - Serial.print(_data.pm25_standard); - Serial.print(F("\t\tPM 10: ")); - Serial.println(_data.pm100_standard); - Serial.println(F("Concentration Units (environmental)")); - Serial.println(F("---------------------------------------")); - Serial.print(F("PM 1.0: ")); - Serial.print(_data.pm10_env); - Serial.print(F("\t\tPM 2.5: ")); - Serial.print(_data.pm25_env); - Serial.print(F("\t\tPM 10: ")); - Serial.println(_data.pm100_env); - Serial.println(F("---------------------------------------")); - - return true; - } - - /*******************************************************************************/ - /*! - @brief Packs and sends the device's event data to Adafruit IO. - */ - /*******************************************************************************/ - void send_data() override { - // Create a new UART response message - wippersnapper_signal_v1_UARTResponse msgUARTResponse = - wippersnapper_signal_v1_UARTResponse_init_zero; - msgUARTResponse.which_payload = - wippersnapper_signal_v1_UARTResponse_resp_uart_device_event_tag; - strcpy(msgUARTResponse.payload.resp_uart_device_event.device_id, - getDriverID()); - - // check if driverID is pm1006 - if (strcmp(getDriverID(), "pm1006") == 0) { - // PM1006 returns only PM2.5_ENV readings - msgUARTResponse.payload.resp_uart_device_event.sensor_event_count = 1; - packUARTResponse(&msgUARTResponse, 0, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_ENV, - (float)_data.pm25_env); - } else { - msgUARTResponse.payload.resp_uart_device_event.sensor_event_count = 6; - packUARTResponse(&msgUARTResponse, 0, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM10_STD, - (float)_data.pm10_standard); - - packUARTResponse(&msgUARTResponse, 1, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_STD, - (float)_data.pm25_standard); - - packUARTResponse(&msgUARTResponse, 2, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM100_STD, - (float)_data.pm100_standard); - - packUARTResponse(&msgUARTResponse, 3, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM10_ENV, - (float)_data.pm10_env); - - packUARTResponse(&msgUARTResponse, 4, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_ENV, - (float)_data.pm25_env); - - packUARTResponse(&msgUARTResponse, 5, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM100_ENV, - (float)_data.pm100_env); - } - - // Encode message data - uint8_t mqttBuffer[512] = {0}; - pb_ostream_t ostream = - pb_ostream_from_buffer(mqttBuffer, sizeof(mqttBuffer)); - if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_UARTResponse_fields, - &msgUARTResponse)) { - Serial.println("[ERROR, UART]: Unable to encode device response!"); - return; - } - - // Publish message to IO - size_t msgSz; - pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_UARTResponse_fields, - &msgUARTResponse); - Serial.print("[UART] Publishing event to IO.."); - mqttClient->publish(uartTopic, mqttBuffer, msgSz, 1); - Serial.println("Published!"); - - setPrvPollTime(millis()); - } - -protected: - Adafruit_PM25AQI *_aqi = nullptr; ///< Pointer to PM25AQI sensor object - PM25_AQI_Data _data; ///< PM25AQI sensor data struct. -#ifdef USE_SW_UART - SoftwareSerial *_swSerial = nullptr; ///< Pointer to Software UART interface -#else - HardwareSerial *_hwSerial = nullptr; ///< Pointer to Hardware UART interface -#endif -}; - +/*! + * @file ws_uart_drv_pm25aqi.h + * + * WipperSnapper device driver for the Adafruit_PM25AQI Arduino Library + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2023 for Adafruit Industries. + * + * MIT license, all text here must be included in any redistribution. + * + */ +#ifndef WS_UART_DRV_PM25AQI_H +#define WS_UART_DRV_PM25AQI_H + +#include "Wippersnapper.h" +#include "ws_uart_drv.h" +#include + +/**************************************************************************/ +/*! + @brief Class that provides an interface for a PM25 AQI UART sensor. +*/ +/**************************************************************************/ +class ws_uart_drv_pm25aqi : public ws_uart_drv { +public: +#ifdef USE_SW_UART + /*******************************************************************************/ + /*! + @brief Initializes a PM25AQI UART device driver. + @param swSerial + Pointer to an instance of a SoftwareSerial object. + @param pollingInterval + How often the PM25AQI device will be polled, in milliseconds. + */ + /*******************************************************************************/ + ws_uart_drv_pm25aqi(SoftwareSerial *swSerial, int32_t interval) + : ws_uart_drv(swSerial, interval) { + _swSerial = swSerial; + pollingInterval = (unsigned long)interval; + }; +#else + /*******************************************************************************/ + /*! + @brief Initializes the PM25AQI UART device driver. + @param hwSerial + Pointer to an instance of a HardwareSerial object. + @param interval + How often the PM25AQI device will be polled, in milliseconds. + */ + /*******************************************************************************/ + ws_uart_drv_pm25aqi(HardwareSerial *hwSerial, int32_t interval) + : ws_uart_drv(hwSerial, interval) { + _hwSerial = hwSerial; + pollingInterval = (unsigned long)interval; + }; +#endif // USE_SW_UART + + /*******************************************************************************/ + /*! + @brief Destructor for a PM25AQI sensor. + */ + /*******************************************************************************/ + ~ws_uart_drv_pm25aqi() { + delete _aqi; +#ifdef USE_SW_UART + _swSerial = nullptr; +#else + _hwSerial = nullptr; +#endif + } + + /*******************************************************************************/ + /*! + @brief Initializes a PM25AQI sensor. + @returns True if the PM25AQI sensor was successfully initialized, + False otherwise. + */ + /*******************************************************************************/ + bool begin() override { + _aqi = new Adafruit_PM25AQI(); +#ifdef USE_SW_UART + if (!_aqi->begin_UART( + _swSerial)) { // connect to the sensor over software serial + return false; + } +#else + if (!_aqi->begin_UART( + _hwSerial)) { // connect to the sensor over hardware serial + return false; + } +#endif + return true; + } + + /*******************************************************************************/ + /*! + @brief Attempts to read data from the PM25AQI sensor. + @returns True if data was successfully read, False otherwise. + */ + /*******************************************************************************/ + bool read_data() override { + Serial.println("[UART, PM25] Reading data..."); + // Attempt to read the PM2.5 Sensor + if (!_aqi->read(&_data)) { + Serial.println("[UART, PM25] Data not available."); + delay(500); + return false; + } + Serial.println("[UART, PM25] Read data OK"); + Serial.println(); + Serial.println(F("---------------------------------------")); + Serial.println(F("Concentration Units (standard)")); + Serial.println(F("---------------------------------------")); + Serial.print(F("PM 1.0: ")); + Serial.print(_data.pm10_standard); + Serial.print(F("\t\tPM 2.5: ")); + Serial.print(_data.pm25_standard); + Serial.print(F("\t\tPM 10: ")); + Serial.println(_data.pm100_standard); + Serial.println(F("Concentration Units (environmental)")); + Serial.println(F("---------------------------------------")); + Serial.print(F("PM 1.0: ")); + Serial.print(_data.pm10_env); + Serial.print(F("\t\tPM 2.5: ")); + Serial.print(_data.pm25_env); + Serial.print(F("\t\tPM 10: ")); + Serial.println(_data.pm100_env); + Serial.println(F("---------------------------------------")); + + return true; + } + + /*******************************************************************************/ + /*! + @brief Packs and sends the device's event data to Adafruit IO. + */ + /*******************************************************************************/ + void send_data() override { + // Create a new UART response message + wippersnapper_signal_v1_UARTResponse msgUARTResponse = + wippersnapper_signal_v1_UARTResponse_init_zero; + msgUARTResponse.which_payload = + wippersnapper_signal_v1_UARTResponse_resp_uart_device_event_tag; + strcpy(msgUARTResponse.payload.resp_uart_device_event.device_id, + getDriverID()); + + // check if driverID is pm1006 + if (strcmp(getDriverID(), "pm1006") == 0) { + // PM1006 returns only PM2.5_ENV readings + msgUARTResponse.payload.resp_uart_device_event.sensor_event_count = 1; + packUARTResponse(&msgUARTResponse, 0, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_ENV, + (float)_data.pm25_env); + } else { + msgUARTResponse.payload.resp_uart_device_event.sensor_event_count = 6; + packUARTResponse(&msgUARTResponse, 0, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM10_STD, + (float)_data.pm10_standard); + + packUARTResponse(&msgUARTResponse, 1, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_STD, + (float)_data.pm25_standard); + + packUARTResponse(&msgUARTResponse, 2, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM100_STD, + (float)_data.pm100_standard); + + packUARTResponse(&msgUARTResponse, 3, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM10_ENV, + (float)_data.pm10_env); + + packUARTResponse(&msgUARTResponse, 4, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_ENV, + (float)_data.pm25_env); + + packUARTResponse(&msgUARTResponse, 5, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM100_ENV, + (float)_data.pm100_env); + } + + // Encode message data + uint8_t mqttBuffer[512] = {0}; + pb_ostream_t ostream = + pb_ostream_from_buffer(mqttBuffer, sizeof(mqttBuffer)); + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_UARTResponse_fields, + &msgUARTResponse)) { + Serial.println("[ERROR, UART]: Unable to encode device response!"); + return; + } + + // Publish message to IO + size_t msgSz; + pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_UARTResponse_fields, + &msgUARTResponse); + Serial.print("[UART] Publishing event to IO.."); + mqttClient->publish(uartTopic, mqttBuffer, msgSz, 1); + Serial.println("Published!"); + + setPrvPollTime(millis()); + } + +protected: + Adafruit_PM25AQI *_aqi = nullptr; ///< Pointer to PM25AQI sensor object + PM25_AQI_Data _data; ///< PM25AQI sensor data struct. +#ifdef USE_SW_UART + SoftwareSerial *_swSerial = nullptr; ///< Pointer to Software UART interface +#else + HardwareSerial *_hwSerial = nullptr; ///< Pointer to Hardware UART interface +#endif +}; + #endif // WS_UART_DRV_PM25AQI_H \ No newline at end of file diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h index 33d43e8e4..aff9115c7 100644 --- a/src/network_interfaces/Wippersnapper_WIFININA.h +++ b/src/network_interfaces/Wippersnapper_WIFININA.h @@ -1,284 +1,284 @@ -/*! - * @file Wippersnapper_WIFININA.h - * - * Network interface for the ublox wifi module on the - * Arduino MKR WiFi 1010, Arduino Nano 33 IoT and Arduino UNO WiFi Rev.2. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2021 for Adafruit Industries. - * - * MIT license, all text here must be included in any redistribution. - * - */ - -#ifndef WIPPERSNAPPER_WIFININA_H -#define WIPPERSNAPPER_WIFININA_H -#include -#include -#include -#include -#include - -#include "Wippersnapper.h" - -#define SPIWIFI \ - SPI /*!< Instance of SPI interface used by an external uBlox module. */ - -extern Wippersnapper WS; -/****************************************************************************/ -/*! - @brief Class for using the AirLift Co-Processor network iface. -*/ -/****************************************************************************/ -class Wippersnapper_WIFININA : public Wippersnapper { - -public: - /**************************************************************************/ - /*! - @brief Initializes the Adafruit IO class for ublox devices. - @param aioUsername - Adafruit IO username - @param aioKey - Adafruit IO key - @param netSSID - Wireless Network SSID - @param netPass - Wireless Network password - */ - /**************************************************************************/ - Wippersnapper_WIFININA(const char *aioUsername, const char *aioKey, - const char *netSSID, const char *netPass) - : Wippersnapper() { - _ssid = netSSID; - _pass = netPass; - _username = aioUsername; - _key = aioKey; - - _wifi = &SPIWIFI; - _mqtt_client = new WiFiSSLClient; - } - - /**************************************************************************/ - /*! - @brief Destructor for the Adafruit IO ublox class. - */ - /**************************************************************************/ - ~Wippersnapper_WIFININA() { - if (_mqtt) - delete _mqtt; - } - - /****************************************************************************/ - /*! - @brief Configures the device's Adafruit IO credentials. This method - should be used only if filesystem-backed provisioning is - not avaliable. - */ - /****************************************************************************/ - void set_user_key() { - strlcpy(WS._config.aio_user, _username, sizeof(WS._config.aio_user)); - strlcpy(WS._config.aio_key, _key, sizeof(WS._config.aio_key)); - } - - /**********************************************************/ - /*! - @brief Sets the WiFi client's ssid and password. - @param ssid - Wireless network's SSID. - @param ssidPassword - Wireless network's password. - */ - /**********************************************************/ - void set_ssid_pass(const char *ssid, const char *ssidPassword) { - strlcpy(WS._config.network.ssid, ssid, sizeof(WS._config.network.ssid)); - strlcpy(WS._config.network.pass, ssidPassword, - sizeof(WS._config.network.pass)); - } - - /**********************************************************/ - /*! - @brief Sets the WiFi client's ssid and password from the - header file's credentials. - */ - /**********************************************************/ - void set_ssid_pass() { - strlcpy(WS._config.network.ssid, _ssid, sizeof(WS._config.network.ssid)); - strlcpy(WS._config.network.pass, _pass, sizeof(WS._config.network.pass)); - } - - /***********************************************************/ - /*! - @brief Performs a scan of local WiFi networks. - @returns True if `_network_ssid` is found, False otherwise. - */ - /***********************************************************/ - bool check_valid_ssid() { - // Set WiFi to station mode and disconnect from an AP if it was previously - // connected - WiFi.disconnect(); - delay(100); - - // Perform a network scan - int n = WiFi.scanNetworks(); - if (n == 0) { - WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!"); - return false; - } - - // Was the network within secrets.json found? - for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i)) == 0) { - return true; - } - } - - // User-set network not found, print scan results to serial console - WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); - WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); - for (int i = 0; i < n; ++i) { - WS_DEBUG_PRINT(WiFi.SSID(i)); - WS_DEBUG_PRINT(" "); - WS_DEBUG_PRINT(WiFi.RSSI(i)); - WS_DEBUG_PRINTLN("dB"); - } - - return false; - } - - /********************************************************/ - /*! - @brief Sets the WiFi client. - @param wifi - Instance of SPIClass. - */ - /********************************************************/ - void set_wifi(SPIClass *wifi) { - _wifi = wifi; - _mqtt_client = new WiFiSSLClient; - } - - /***********************************************************/ - /*! - @brief Checks the nina-fw version on the module. - @return True if firmware on the ublox module matches - the latest version of the library, False otherwise. - */ - /***********************************************************/ - bool firmwareCheck() { - String fv = WiFi.firmwareVersion(); - if (fv < WIFI_FIRMWARE_LATEST_VERSION) - return false; - return true; - } - - /********************************************************/ - /*! - @brief Gets the ESP32's unique client identifier. - @note For the ESP32, the UID is the MAC address. - */ - /********************************************************/ - void getMacAddr() { - uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - WiFi.macAddress(mac); - 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. - @param clientID - MQTT client identifier - */ - /********************************************************/ - void setupMQTTClient(const char *clientID) { - 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); - } - - /********************************************************/ - /*! - @brief Returns the network status of an ESP32 module. - @return ws_status_t - */ - /********************************************************/ - ws_status_t networkStatus() { - switch (WiFi.status()) { - case WL_CONNECTED: - return WS_NET_CONNECTED; - case WL_CONNECT_FAILED: - return WS_NET_CONNECT_FAILED; - case WL_IDLE_STATUS: - return WS_IDLE; - default: - return WS_NET_DISCONNECTED; - } - } - - /*******************************************************************/ - /*! - @brief Returns the type of network connection used by Wippersnapper - @return AIRLIFT - */ - /*******************************************************************/ - const char *connectionType() { return "AIRLIFT"; } - -protected: - const char *_ssid; /*!< Network SSID. */ - const char *_pass; /*!< Network password. */ - const char *_username; /*!< Adafruit IO username. */ - const char *_key; /*!< Adafruit IO key. */ - - WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */ - SPIClass *_wifi; /*!< Instance of the SPI bus used by the ublox. */ - - /**************************************************************************/ - /*! - @brief Establishes a connection with the wireless network. - */ - /**************************************************************************/ - void _connect() { - - // check if co-processor connected first - if (WiFi.status() == WL_NO_MODULE) - errorWriteHang("No WiFi Module Detected!"); - - // validate the nina-fw version - if (!firmwareCheck()) - errorWriteHang("Please upgrade the firmware on the ESP module to the " - "latest version."); - - if (strlen(_ssid) == 0) { - _status = WS_SSID_INVALID; - } else { - // disconnect from possible previous connection - _disconnect(); - - WiFi.begin(_ssid, _pass); - _status = WS_NET_DISCONNECTED; - } - } - - /**************************************************************************/ - /*! - @brief Disconnects from the wireless network. - */ - /**************************************************************************/ - void _disconnect() { - WiFi.disconnect(); - delay(500); - } -}; - +/*! + * @file Wippersnapper_WIFININA.h + * + * Network interface for the ublox wifi module on the + * Arduino MKR WiFi 1010, Arduino Nano 33 IoT and Arduino UNO WiFi Rev.2. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2021 for Adafruit Industries. + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef WIPPERSNAPPER_WIFININA_H +#define WIPPERSNAPPER_WIFININA_H +#include +#include +#include +#include +#include + +#include "Wippersnapper.h" + +#define SPIWIFI \ + SPI /*!< Instance of SPI interface used by an external uBlox module. */ + +extern Wippersnapper WS; +/****************************************************************************/ +/*! + @brief Class for using the AirLift Co-Processor network iface. +*/ +/****************************************************************************/ +class Wippersnapper_WIFININA : public Wippersnapper { + +public: + /**************************************************************************/ + /*! + @brief Initializes the Adafruit IO class for ublox devices. + @param aioUsername + Adafruit IO username + @param aioKey + Adafruit IO key + @param netSSID + Wireless Network SSID + @param netPass + Wireless Network password + */ + /**************************************************************************/ + Wippersnapper_WIFININA(const char *aioUsername, const char *aioKey, + const char *netSSID, const char *netPass) + : Wippersnapper() { + _ssid = netSSID; + _pass = netPass; + _username = aioUsername; + _key = aioKey; + + _wifi = &SPIWIFI; + _mqtt_client = new WiFiSSLClient; + } + + /**************************************************************************/ + /*! + @brief Destructor for the Adafruit IO ublox class. + */ + /**************************************************************************/ + ~Wippersnapper_WIFININA() { + if (_mqtt) + delete _mqtt; + } + + /****************************************************************************/ + /*! + @brief Configures the device's Adafruit IO credentials. This method + should be used only if filesystem-backed provisioning is + not avaliable. + */ + /****************************************************************************/ + void set_user_key() { + strlcpy(WS._config.aio_user, _username, sizeof(WS._config.aio_user)); + strlcpy(WS._config.aio_key, _key, sizeof(WS._config.aio_key)); + } + + /**********************************************************/ + /*! + @brief Sets the WiFi client's ssid and password. + @param ssid + Wireless network's SSID. + @param ssidPassword + Wireless network's password. + */ + /**********************************************************/ + void set_ssid_pass(const char *ssid, const char *ssidPassword) { + strlcpy(WS._config.network.ssid, ssid, sizeof(WS._config.network.ssid)); + strlcpy(WS._config.network.pass, ssidPassword, + sizeof(WS._config.network.pass)); + } + + /**********************************************************/ + /*! + @brief Sets the WiFi client's ssid and password from the + header file's credentials. + */ + /**********************************************************/ + void set_ssid_pass() { + strlcpy(WS._config.network.ssid, _ssid, sizeof(WS._config.network.ssid)); + strlcpy(WS._config.network.pass, _pass, sizeof(WS._config.network.pass)); + } + + /***********************************************************/ + /*! + @brief Performs a scan of local WiFi networks. + @returns True if `_network_ssid` is found, False otherwise. + */ + /***********************************************************/ + bool check_valid_ssid() { + // Set WiFi to station mode and disconnect from an AP if it was previously + // connected + WiFi.disconnect(); + delay(100); + + // Perform a network scan + int n = WiFi.scanNetworks(); + if (n == 0) { + WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!"); + return false; + } + + // Was the network within secrets.json found? + for (int i = 0; i < n; ++i) { + if (strcmp(_ssid, WiFi.SSID(i)) == 0) { + return true; + } + } + + // User-set network not found, print scan results to serial console + WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); + WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); + for (int i = 0; i < n; ++i) { + WS_DEBUG_PRINT(WiFi.SSID(i)); + WS_DEBUG_PRINT(" "); + WS_DEBUG_PRINT(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN("dB"); + } + + return false; + } + + /********************************************************/ + /*! + @brief Sets the WiFi client. + @param wifi + Instance of SPIClass. + */ + /********************************************************/ + void set_wifi(SPIClass *wifi) { + _wifi = wifi; + _mqtt_client = new WiFiSSLClient; + } + + /***********************************************************/ + /*! + @brief Checks the nina-fw version on the module. + @return True if firmware on the ublox module matches + the latest version of the library, False otherwise. + */ + /***********************************************************/ + bool firmwareCheck() { + String fv = WiFi.firmwareVersion(); + if (fv < WIFI_FIRMWARE_LATEST_VERSION) + return false; + return true; + } + + /********************************************************/ + /*! + @brief Gets the ESP32's unique client identifier. + @note For the ESP32, the UID is the MAC address. + */ + /********************************************************/ + void getMacAddr() { + uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + WiFi.macAddress(mac); + 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. + @param clientID + MQTT client identifier + */ + /********************************************************/ + void setupMQTTClient(const char *clientID) { + 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); + } + + /********************************************************/ + /*! + @brief Returns the network status of an ESP32 module. + @return ws_status_t + */ + /********************************************************/ + ws_status_t networkStatus() { + switch (WiFi.status()) { + case WL_CONNECTED: + return WS_NET_CONNECTED; + case WL_CONNECT_FAILED: + return WS_NET_CONNECT_FAILED; + case WL_IDLE_STATUS: + return WS_IDLE; + default: + return WS_NET_DISCONNECTED; + } + } + + /*******************************************************************/ + /*! + @brief Returns the type of network connection used by Wippersnapper + @return AIRLIFT + */ + /*******************************************************************/ + const char *connectionType() { return "AIRLIFT"; } + +protected: + const char *_ssid; /*!< Network SSID. */ + const char *_pass; /*!< Network password. */ + const char *_username; /*!< Adafruit IO username. */ + const char *_key; /*!< Adafruit IO key. */ + + WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */ + SPIClass *_wifi; /*!< Instance of the SPI bus used by the ublox. */ + + /**************************************************************************/ + /*! + @brief Establishes a connection with the wireless network. + */ + /**************************************************************************/ + void _connect() { + + // check if co-processor connected first + if (WiFi.status() == WL_NO_MODULE) + errorWriteHang("No WiFi Module Detected!"); + + // validate the nina-fw version + if (!firmwareCheck()) + errorWriteHang("Please upgrade the firmware on the ESP module to the " + "latest version."); + + if (strlen(_ssid) == 0) { + _status = WS_SSID_INVALID; + } else { + // disconnect from possible previous connection + _disconnect(); + + WiFi.begin(_ssid, _pass); + _status = WS_NET_DISCONNECTED; + } + } + + /**************************************************************************/ + /*! + @brief Disconnects from the wireless network. + */ + /**************************************************************************/ + void _disconnect() { + WiFi.disconnect(); + delay(500); + } +}; + #endif // WIPPERSNAPPER_WIFININA_H \ No newline at end of file From cf850bb5ce1d27cb521b019bb62bc6f7e8d0ddfd Mon Sep 17 00:00:00 2001 From: tyeth Date: Tue, 2 Jul 2024 15:04:09 +0100 Subject: [PATCH 42/54] Change to LN to avoid whitespace showing in diff --- .gitignore | 100 ++++++++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/.gitignore b/.gitignore index b4c22ca55..b22a8d404 100644 --- a/.gitignore +++ b/.gitignore @@ -1,51 +1,51 @@ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - -# Doxygen -*.bak -html/* - - -# VSCode artifacts -.vscode/* -src/.vscode/settings.json - -.DS_STORE - -examples/Wippersnapper_demo/build/ - -# Platformio artifacts -.pio/ - -# Secrets +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Doxygen +*.bak +html/* + + +# VSCode artifacts +.vscode/* +src/.vscode/settings.json + +.DS_STORE + +examples/Wippersnapper_demo/build/ + +# Platformio artifacts +.pio/ + +# Secrets data/ \ No newline at end of file From 0a174a9e0ab964152dfd8be997e1f9aa1a03f9e1 Mon Sep 17 00:00:00 2001 From: tyeth Date: Tue, 2 Jul 2024 15:12:59 +0100 Subject: [PATCH 43/54] Correct initialisation of firmware variables in compareVersions --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index e51829bb1..0fb57eaee 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -195,8 +195,8 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { /********************************************************/ bool compareVersions(const char *currentVersion, const char *requiredVersion) { - int curMajor, curMinor, curPatch = 0; - int reqMajor, reqMinor, reqPatch = 0; + 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)) { From ac31ff915f54ea16f6d0e964090fa1d03616bf52 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Wed, 3 Jul 2024 01:19:21 +0100 Subject: [PATCH 44/54] Fix: calls to getRSSI unqualified inside Wippersnapper.cpp + only get RSSI before connect in check_valid_ssid --- src/Wippersnapper.cpp | 6 ++---- src/network_interfaces/Wippersnapper_AIRLIFT.h | 4 +++- src/network_interfaces/Wippersnapper_ESP32.h | 2 ++ src/network_interfaces/Wippersnapper_ESP8266.h | 2 ++ src/network_interfaces/Wippersnapper_WIFININA.h | 2 ++ src/network_interfaces/ws_networking_pico.h | 2 ++ 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 7453ece39..ce9329588 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -2402,8 +2402,6 @@ void Wippersnapper::runNetFSM() { haltError("ERROR: Unable to find WiFi network, rebooting soon...", WS_LED_STATUS_WIFI_CONNECTING); } - WS_DEBUG_PRINT("SSID found! RSSI: "); - WS_DEBUG_PRINTLN(WS.getRSSI()); // Attempt to connect to wireless network maxAttempts = 5; while (maxAttempts > 0) { @@ -2570,7 +2568,7 @@ void Wippersnapper::pingBroker() { } _prv_ping = millis(); WS_DEBUG_PRINT("WiFi RSSI: "); - WS_DEBUG_PRINTLN(WS.getRSSI()); + WS_DEBUG_PRINTLN(getRSSI()); } // blink status LED every STATUS_LED_KAT_BLINK_TIME millis if (millis() > (_prvKATBlink + STATUS_LED_KAT_BLINK_TIME)) { @@ -2887,4 +2885,4 @@ ws_status_t Wippersnapper::run() { WS.feedWDT(); return WS_NET_CONNECTED; // TODO: Make this funcn void! -} \ No newline at end of file +} diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 0fb57eaee..a102c357c 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -120,6 +120,8 @@ 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) { + WS_DEBUG_PRINT("SSID found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; } } @@ -331,7 +333,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { _status = WS_NET_DISCONNECTED; // Use the macro to retry the status check until connected / timed out - int lastResult; + int lastResult = -1; RETRY_FUNCTION_UNTIL_TIMEOUT( []() -> int { return WiFi.status(); }, // Function call each cycle int, // return type diff --git a/src/network_interfaces/Wippersnapper_ESP32.h b/src/network_interfaces/Wippersnapper_ESP32.h index 2f4f2b4cb..26915c112 100644 --- a/src/network_interfaces/Wippersnapper_ESP32.h +++ b/src/network_interfaces/Wippersnapper_ESP32.h @@ -110,6 +110,8 @@ 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 found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; } } diff --git a/src/network_interfaces/Wippersnapper_ESP8266.h b/src/network_interfaces/Wippersnapper_ESP8266.h index 5e48a9b33..e74aed926 100644 --- a/src/network_interfaces/Wippersnapper_ESP8266.h +++ b/src/network_interfaces/Wippersnapper_ESP8266.h @@ -133,6 +133,8 @@ 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 found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; } } diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h index aff9115c7..74f50bffe 100644 --- a/src/network_interfaces/Wippersnapper_WIFININA.h +++ b/src/network_interfaces/Wippersnapper_WIFININA.h @@ -131,6 +131,8 @@ 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) { + WS_DEBUG_PRINT("SSID found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; } } diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index efd513051..5bf7c8988 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -109,6 +109,8 @@ 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 found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; } } From 5bbbe4010aff5ddd5395e829e8939d475e26fb41 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 25 Jul 2024 13:00:31 +0100 Subject: [PATCH 45/54] RETRY_FUNCTION: Correct rollover check --- src/Wippersnapper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index 42c70d6cf..fc807290f 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -100,7 +100,7 @@ if (condition(result_var)) { \ break; \ } \ - if (startTime < millis()) { \ + if (startTime > millis()) { \ startTime = millis(); /* if rollover */ \ } \ WS_DELAY_WITH_WDT(interval); \ From 4f79f7bfb1ca4e163d66c56da65adb1c9aebf109 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 25 Jul 2024 13:07:00 +0100 Subject: [PATCH 46/54] AirLift: define magic numbers for retry timings --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index a102c357c..0facc862a 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -29,6 +29,8 @@ #define NINAFWVER \ "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. */ @@ -339,8 +341,8 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { int, // return type lastResult, // return variable [](int status) { return status == WL_CONNECTED; }, // check - 20000, // timeout interval (ms) - 200); // interval between retries + AIRLIFT_CONNECT_TIMEOUT_MS, // timeout interval (ms) + AIRLIFT_CONNECT_RETRY_DELAY_MS); // interval between retries if (lastResult == WL_CONNECTED) { _status = WS_NET_CONNECTED; From 0d959cd3be381650bce9b10de70dbaa3d81f8cc9 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 25 Jul 2024 13:13:22 +0100 Subject: [PATCH 47/54] AirLift: initialise placeholder firmware version --- src/network_interfaces/Wippersnapper_AIRLIFT.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 0facc862a..4c564b4e1 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -285,7 +285,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { protected: const char *_ssid; /*!< Network SSID. */ const char *_pass; /*!< Network password. */ - const char *_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. */ From d0bb5c5a40dd59894b33aff4b4ad26460822795b Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 25 Jul 2024 13:35:12 +0100 Subject: [PATCH 48/54] Define magic numbers for timeout/retry + print SSID in multi mode --- src/network_interfaces/Wippersnapper_ESP32.h | 8 ++++++- .../Wippersnapper_ESP8266.h | 13 ++++++++--- src/network_interfaces/ws_networking_pico.h | 22 ++++++++++++++----- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_ESP32.h b/src/network_interfaces/Wippersnapper_ESP32.h index f65b6638e..01056ea4d 100644 --- a/src/network_interfaces/Wippersnapper_ESP32.h +++ b/src/network_interfaces/Wippersnapper_ESP32.h @@ -111,7 +111,9 @@ 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 found! RSSI: "); + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(_ssid); + WS_DEBUG_PRINT(") found! RSSI: "); WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; } @@ -119,6 +121,10 @@ class Wippersnapper_ESP32 : public Wippersnapper { // 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; } } diff --git a/src/network_interfaces/Wippersnapper_ESP8266.h b/src/network_interfaces/Wippersnapper_ESP8266.h index 8d44ed4fa..c7fa6b693 100644 --- a/src/network_interfaces/Wippersnapper_ESP8266.h +++ b/src/network_interfaces/Wippersnapper_ESP8266.h @@ -136,15 +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 found! RSSI: "); + 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; + } } } } @@ -257,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/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index 4b4837a2f..c58d1b3dd 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -18,6 +18,10 @@ #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" @@ -108,15 +112,21 @@ 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 found! RSSI: "); + 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)) == 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; } } @@ -309,7 +319,7 @@ class ws_networking_pico : public Wippersnapper { WS.feedWDT(); } else { WiFi.begin(_ssid, _pass); - + // Use the macro to retry the status check until connected / timed out int lastResult; RETRY_FUNCTION_UNTIL_TIMEOUT( @@ -317,8 +327,8 @@ class ws_networking_pico : public Wippersnapper { int, // return type lastResult, // return variable (unused here) [](int status) { return status == WL_CONNECTED; }, // check - 20000, // timeout interval (ms) - 200); // interval between retries + PICO_CONNECT_TIMEOUT_MS, // timeout interval (ms) + PICO_CONNECT_RETRY_DELAY_MS); // interval between retries if (lastResult == WL_CONNECTED) { _status = WS_NET_CONNECTED; From 7c35b540e42a20d64d7c783f92f01480600acd9a Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 25 Jul 2024 13:37:59 +0100 Subject: [PATCH 49/54] Update staging certificates for testing --- src/network_interfaces/Wippersnapper_ESP32.h | 48 ++++++++++---------- src/network_interfaces/ws_networking_pico.h | 48 ++++++++++---------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_ESP32.h b/src/network_interfaces/Wippersnapper_ESP32.h index 01056ea4d..6c4193429 100644 --- a/src/network_interfaces/Wippersnapper_ESP32.h +++ b/src/network_interfaces/Wippersnapper_ESP32.h @@ -221,30 +221,30 @@ class Wippersnapper_ESP32 : public Wippersnapper { 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/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index c58d1b3dd..f7a7fcd80 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -224,30 +224,30 @@ class ws_networking_pico : public Wippersnapper { 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 = From fa035f89d756a658393b994a0f663d897ce94a83 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 25 Jul 2024 14:13:46 +0100 Subject: [PATCH 50/54] Pico: remove unnecessary c_str calls --- src/network_interfaces/ws_networking_pico.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index f7a7fcd80..e10f1a086 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -112,7 +112,7 @@ 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).c_str()) == 0) { + if (strcmp(_ssid, WiFi.SSID(i)) == 0) { WS_DEBUG_PRINT("SSID ("); WS_DEBUG_PRINT(_ssid); WS_DEBUG_PRINT(") found! RSSI: "); @@ -122,7 +122,7 @@ class ws_networking_pico : public Wippersnapper { 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)) == 0) { WS_DEBUG_PRINT("SSID ("); WS_DEBUG_PRINT(WS._multiNetworks[j].ssid); WS_DEBUG_PRINT(") found! RSSI: "); From 19e634a76989a876b031d0762755b75ca734cc97 Mon Sep 17 00:00:00 2001 From: brentru Date: Wed, 24 Jul 2024 12:35:33 -0400 Subject: [PATCH 51/54] new ssl cert, NetWorkClient update --- src/network_interfaces/Wippersnapper_ESP32.h | 49 ++++++++++++-------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/src/network_interfaces/Wippersnapper_ESP32.h b/src/network_interfaces/Wippersnapper_ESP32.h index 6c4193429..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; } /********************************************************/ @@ -172,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); } /********************************************************/ @@ -214,10 +222,13 @@ 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" From 2e77858912b65dd183045f6679ce4dc9fcc927d4 Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 25 Jul 2024 11:36:20 -0400 Subject: [PATCH 52/54] add wifi changes to pico, fix pio ini to avoid compile override --- platformio.ini | 2 +- src/network_interfaces/ws_networking_pico.h | 44 ++++++++++++--------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/platformio.ini b/platformio.ini index 714015e3d..fc882d2e6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -110,7 +110,7 @@ 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 diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index e10f1a086..283a24467 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -27,6 +27,7 @@ #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #include "Arduino.h" +#include #include extern Wippersnapper WS; @@ -46,7 +47,6 @@ class ws_networking_pico : public Wippersnapper { ws_networking_pico() : Wippersnapper() { _ssid = 0; _pass = 0; - _mqtt_client = new WiFiClientSecure; } /**************************************************************************/ @@ -55,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; } /********************************************************/ @@ -174,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); } /********************************************************/ @@ -217,10 +222,13 @@ 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" From ad3c019ea6c359ab89895673e2a3233f95df86d3 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 25 Jul 2024 20:59:43 +0100 Subject: [PATCH 53/54] Ignore Wifi101 on ESP32 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index fc882d2e6..f154f0f23 100644 --- a/platformio.ini +++ b/platformio.ini @@ -82,7 +82,7 @@ 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 From 4d1c2045565b0f654b98510626e81f4e49410b2a Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 25 Jul 2024 21:14:58 +0100 Subject: [PATCH 54/54] Add WiFi101 to lib_ignore for ESP8266 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index f154f0f23..1ed2b6d54 100644 --- a/platformio.ini +++ b/platformio.ini @@ -88,7 +88,7 @@ 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]