Skip to content

Commit bd8a96a

Browse files
authored
Merge pull request #493 from brentru/add-uart-feature
Feature: Add support for UART devices
2 parents 47149c4 + cab36d9 commit bd8a96a

File tree

11 files changed

+924
-53
lines changed

11 files changed

+924
-53
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
"files.associations": {
33
"limits": "c",
44
"type_traits": "c"
5-
}
5+
},
6+
"C_Cpp.dimInactiveRegions": false
67
}

platformio.ini

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ framework = arduino
1616
monitor_speed = 115200
1717
lib_compat_mode = strict
1818
lib_deps =
19+
adafruit/Adafruit Zero DMA Library
1920
adafruit/Adafruit TinyUSB Library
2021
adafruit/Adafruit NeoPixel
21-
adafruit/Adafruit Zero DMA Library
2222
adafruit/Adafruit SPIFlash
2323
adafruit/Adafruit DotStar
2424
adafruit/ENS160 - Adafruit Fork
@@ -71,10 +71,16 @@ platform = espressif32 @ ^6.3.2
7171
lib_ignore = WiFiNINA
7272
monitor_filters = esp32_exception_decoder, time
7373

74+
; Common build environment for ESP8266 platform
7475
[common:esp8266]
7576
platform = espressif8266
7677
lib_ignore = WiFiNINA, Adafruit TinyUSB Library
7778

79+
; Common build environment for Atmel/Microchip SAMDx platform
80+
[common:atsamd]
81+
platform = atmelsam
82+
lib_ldf_mode = deep
83+
7884
; ESP32-x Boards ;
7985

8086
; Adafruit ESP32 Feather
@@ -186,23 +192,29 @@ extra_scripts = pre:rename_usb_config.py
186192
extends=common:esp8266
187193
board = huzzah
188194
build_flags = -DARDUINO_ESP8266_ADAFRUIT_HUZZAH
195+
board_build.filesystem = littlefs
196+
upload_port = /dev/cu.SLAB_USBtoUART
189197

190198
; SAMD51 Boards ;
191199

192200
; Adafruit PyPortal M4
193201
[env:adafruit_pyportal_m4]
194-
platform = atmelsam
202+
extends = common:atsamd
195203
board = adafruit_pyportal_m4
196-
lib_ldf_mode = deep+ ; Required for the inclusion of ZeroDMA for some reason
197204
build_flags = -DUSE_TINYUSB=1
198205
-DADAFRUIT_PYPORTAL
199-
;upload_port=/dev/cu./dev/cu.usbmodem13301
200206

201207
; Adafruit PyPortal M4 Titano
202208
[env:adafruit_pyportal_m4_titano]
203-
platform = atmelsam
209+
extends = common:atsamd
204210
board = adafruit_pyportal_m4_titano
205-
lib_ldf_mode = deep+ ; Required for the inclusion of ZeroDMA for some reason
206211
build_flags = -DUSE_TINYUSB=1
207212
-DADAFRUIT_PYPORTAL_M4_TITANO
208-
;upload_port=/dev/cu./dev/cu.usbmodem13301
213+
214+
; Adafruit Metro M4 Airlift Lite
215+
[env:adafruit_metro_m4_airliftlite]
216+
extends = common:atsamd
217+
board = adafruit_metro_m4_airliftlite
218+
build_flags = -DUSE_TINYUSB=1
219+
-DADAFRUIT_METRO_M4_AIRLIFT_LITE
220+
upload_port = /dev/cu.usbmodem1201

src/Wippersnapper.cpp

Lines changed: 186 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,26 @@ Wippersnapper::Wippersnapper() {
5353
_throttle_sub = 0;
5454

5555
// Init. component classes
56-
57-
// DallasSemi (OneWire)
58-
WS._ds18x20Component = new ws_ds18x20();
5956
// LEDC (ESP32-ONLY)
6057
#ifdef ARDUINO_ARCH_ESP32
6158
WS._ledc = new ws_ledc();
6259
#endif
63-
// Servo
64-
WS._servoComponent = new ws_servo();
65-
// PWM
60+
61+
// PWM (Arch-specific implementations)
6662
#ifdef ARDUINO_ARCH_ESP32
6763
WS._pwmComponent = new ws_pwm(WS._ledc);
6864
#else
6965
WS._pwmComponent = new ws_pwm();
7066
#endif
67+
68+
// Servo
69+
WS._servoComponent = new ws_servo();
70+
71+
// UART
72+
WS._uartComponent = new ws_uart();
73+
74+
// DallasSemi (OneWire)
75+
WS._ds18x20Component = new ws_ds18x20();
7176
};
7277

7378
/**************************************************************************/
@@ -1476,6 +1481,126 @@ void cbPixelsMsg(char *data, uint16_t len) {
14761481
WS_DEBUG_PRINTLN("ERROR: Unable to decode pixel topic message");
14771482
}
14781483

1484+
/******************************************************************************************/
1485+
/*!
1486+
@brief Decodes a UART message and executes the callback based on the
1487+
message's tag.
1488+
@param stream
1489+
Incoming data stream from buffer.
1490+
@param field
1491+
Protobuf message's tag type.
1492+
@param arg
1493+
Optional arguments from decoder calling function.
1494+
@returns True if decoded successfully, False otherwise.
1495+
*/
1496+
/******************************************************************************************/
1497+
bool cbDecodeUARTMessage(pb_istream_t *stream, const pb_field_t *field,
1498+
void **arg) {
1499+
if (field->tag ==
1500+
wippersnapper_signal_v1_UARTRequest_req_uart_device_attach_tag) {
1501+
WS_DEBUG_PRINTLN(
1502+
"[Message Type]: "
1503+
"wippersnapper_signal_v1_UARTRequest_req_uart_device_attach_tag");
1504+
1505+
// attempt to decode create message
1506+
wippersnapper_uart_v1_UARTDeviceAttachRequest msgUARTInitReq =
1507+
wippersnapper_uart_v1_UARTDeviceAttachRequest_init_zero;
1508+
if (!pb_decode(stream, wippersnapper_uart_v1_UARTDeviceAttachRequest_fields,
1509+
&msgUARTInitReq)) {
1510+
WS_DEBUG_PRINTLN(
1511+
"ERROR: Could not decode message of type: UARTDeviceAttachRequest!");
1512+
return false;
1513+
}
1514+
1515+
// Check if bus_info is within the message
1516+
if (!msgUARTInitReq.has_bus_info) {
1517+
WS_DEBUG_PRINTLN("ERROR: UART bus info not found within message!");
1518+
return false;
1519+
}
1520+
1521+
// Have we previously initialized the UART bus?
1522+
if (!WS._uartComponent->isUARTBusInitialized())
1523+
WS._uartComponent->initUARTBus(&msgUARTInitReq); // Init. UART bus
1524+
1525+
// Attach UART device to the bus specified in the message
1526+
bool did_begin = WS._uartComponent->initUARTDevice(&msgUARTInitReq);
1527+
1528+
// Create a UARTResponse message
1529+
wippersnapper_signal_v1_UARTResponse msgUARTResponse =
1530+
wippersnapper_signal_v1_UARTResponse_init_zero;
1531+
msgUARTResponse.which_payload =
1532+
wippersnapper_signal_v1_UARTResponse_resp_uart_device_attach_tag;
1533+
msgUARTResponse.payload.resp_uart_device_attach.is_success = did_begin;
1534+
strcpy(msgUARTResponse.payload.resp_uart_device_attach.device_id,
1535+
msgUARTInitReq.device_id);
1536+
memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing));
1537+
pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing,
1538+
sizeof(WS._buffer_outgoing));
1539+
if (!pb_encode(&ostream, wippersnapper_signal_v1_UARTResponse_fields,
1540+
&msgUARTResponse)) {
1541+
WS_DEBUG_PRINTLN("ERROR: Unable to encode UART response message!");
1542+
return false;
1543+
}
1544+
size_t msgSz; // message's encoded size
1545+
pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_UARTResponse_fields,
1546+
&msgUARTResponse);
1547+
WS_DEBUG_PRINT("PUBLISHING: UART Attach Response...");
1548+
WS._mqtt->publish(WS._topic_signal_uart_device, WS._buffer_outgoing, msgSz,
1549+
1);
1550+
WS_DEBUG_PRINTLN("Published!");
1551+
1552+
} else if (field->tag ==
1553+
wippersnapper_signal_v1_UARTRequest_req_uart_device_detach_tag) {
1554+
WS_DEBUG_PRINTLN("[New Message] UART Detach");
1555+
// attempt to decode uart detach request message
1556+
wippersnapper_uart_v1_UARTDeviceDetachRequest msgUARTDetachReq =
1557+
wippersnapper_uart_v1_UARTDeviceDetachRequest_init_zero;
1558+
if (!pb_decode(stream, wippersnapper_uart_v1_UARTDeviceDetachRequest_fields,
1559+
&msgUARTDetachReq)) {
1560+
WS_DEBUG_PRINTLN("ERROR: Could not decode message!");
1561+
return false;
1562+
}
1563+
// detach UART device
1564+
WS._uartComponent->detachUARTDevice(&msgUARTDetachReq);
1565+
WS_DEBUG_PRINTLN("Detached uart device from bus");
1566+
} else {
1567+
WS_DEBUG_PRINTLN("ERROR: UART message type not found!");
1568+
return false;
1569+
}
1570+
return true;
1571+
}
1572+
1573+
/**************************************************************************/
1574+
/*!
1575+
@brief Called when the signal UART sub-topic receives a
1576+
new message. Performs decoding.
1577+
@param data
1578+
Incoming data from MQTT broker.
1579+
@param len
1580+
Length of incoming data.
1581+
*/
1582+
/**************************************************************************/
1583+
void cbSignalUARTReq(char *data, uint16_t len) {
1584+
WS_DEBUG_PRINTLN("* NEW MESSAGE on Signal of type UART: ");
1585+
WS_DEBUG_PRINT(len);
1586+
WS_DEBUG_PRINTLN(" bytes.");
1587+
// zero-out current buffer
1588+
memset(WS._buffer, 0, sizeof(WS._buffer));
1589+
// copy mqtt data into buffer
1590+
memcpy(WS._buffer, data, len);
1591+
WS.bufSize = len;
1592+
1593+
// Set up the payload callback, which will set up the callbacks for
1594+
// each oneof payload field once the field tag is known
1595+
WS.msgSignalUART.cb_payload.funcs.decode = cbDecodeUARTMessage;
1596+
1597+
// Decode DS signal request
1598+
pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize);
1599+
if (!pb_decode(&istream, wippersnapper_signal_v1_UARTRequest_fields,
1600+
&WS.msgSignalUART))
1601+
WS_DEBUG_PRINTLN("ERROR: Unable to decode UART Signal message");
1602+
}
1603+
14791604
/****************************************************************************/
14801605
/*!
14811606
@brief Handles MQTT messages on signal topic until timeout.
@@ -2125,6 +2250,58 @@ bool Wippersnapper::generateWSTopics() {
21252250
return false;
21262251
}
21272252

2253+
// Create device-to-broker UART topic
2254+
2255+
// Calculate size for dynamic MQTT topic
2256+
size_t topicLen = strlen(WS._username) + strlen("/") + strlen(_device_uid) +
2257+
strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) +
2258+
strlen("broker/uart") + 1;
2259+
2260+
// Allocate memory for dynamic MQTT topic
2261+
#ifdef USE_PSRAM
2262+
WS._topic_signal_uart_brkr = (char *)ps_malloc(topicLen);
2263+
#else
2264+
WS._topic_signal_uart_brkr = (char *)malloc(topicLen);
2265+
#endif
2266+
2267+
// Generate the topic if memory was allocated successfully
2268+
if (WS._topic_signal_uart_brkr != NULL) {
2269+
snprintf(WS._topic_signal_uart_brkr, topicLen, "%s/wprsnpr/%s%sbroker/uart",
2270+
WS._username, _device_uid, TOPIC_SIGNALS);
2271+
} else {
2272+
WS_DEBUG_PRINTLN("FATAL ERROR: Failed to allocate memory for UART topic!");
2273+
return false;
2274+
}
2275+
2276+
// Subscribe to signal's UART sub-topic
2277+
_topic_signal_uart_sub =
2278+
new Adafruit_MQTT_Subscribe(WS._mqtt, WS._topic_signal_uart_brkr, 1);
2279+
WS._mqtt->subscribe(_topic_signal_uart_sub);
2280+
// Set MQTT callback function
2281+
_topic_signal_uart_sub->setCallback(cbSignalUARTReq);
2282+
2283+
// Create broker-to-device UART topic
2284+
// Calculate size for dynamic MQTT topic
2285+
topicLen = strlen(WS._username) + strlen("/") + strlen(_device_uid) +
2286+
strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) +
2287+
strlen("device/uart") + 1;
2288+
2289+
// Allocate memory for dynamic MQTT topic
2290+
#ifdef USE_PSRAM
2291+
WS._topic_signal_uart_device = (char *)ps_malloc(topicLen);
2292+
#else
2293+
WS._topic_signal_uart_device = (char *)malloc(topicLen);
2294+
#endif
2295+
2296+
// Generate the topic if memory was allocated successfully
2297+
if (WS._topic_signal_uart_device != NULL) {
2298+
snprintf(WS._topic_signal_uart_device, topicLen,
2299+
"%s/wprsnpr/%s%sdevice/uart", WS._username, _device_uid,
2300+
TOPIC_SIGNALS);
2301+
} else {
2302+
WS_DEBUG_PRINTLN("FATAL ERROR: Failed to allocate memory for UART topic!");
2303+
return false;
2304+
}
21282305
return true;
21292306
}
21302307

@@ -2661,5 +2838,8 @@ ws_status_t Wippersnapper::run() {
26612838
// Process DS18x20 sensor events
26622839
WS._ds18x20Component->update();
26632840

2841+
// Process UART sensor events
2842+
WS._uartComponent->update();
2843+
26642844
return WS_NET_CONNECTED; // TODO: Make this funcn void!
26652845
}

0 commit comments

Comments
 (0)