Skip to content

Commit 9f0115f

Browse files
authored
Merge pull request #104 from adafruit/i2c-scan
Add I2C Initialization and Scan Workflow
2 parents 9914529 + 4dae9b7 commit 9f0115f

File tree

11 files changed

+706
-12
lines changed

11 files changed

+706
-12
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,5 @@
3838
*.py
3939

4040
# VSCode artifacts
41-
./vscode/*
41+
./vscode/*
42+
src/.vscode/settings.json

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ paragraph=Arduino library for Adafruit.io WipperSnapper
77
category=Communication
88
url=https://github.com/adafruit/Adafruit_IO_Arduino
99
architectures=*
10-
depends=Adafruit NeoPixel, Adafruit SPIFlash, ArduinoJson, Adafruit DotStar, Adafruit SleepyDog Library, Adafruit TinyUSB Library
10+
depends=Adafruit NeoPixel, Adafruit SPIFlash, ArduinoJson, Adafruit DotStar, Adafruit SleepyDog Library, Adafruit TinyUSB Library, Adafruit AHTX0

src/Wippersnapper.cpp

Lines changed: 264 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,224 @@ void cbSignalTopic(char *data, uint16_t len) {
475475
}
476476
}
477477

478-
// TODO: This should be moved into digitalGPIO!
478+
/******************************************************************************************/
479+
/*!
480+
@brief Publishes an I2C response signal message to the broker.
481+
@param msgi2cResponse
482+
A pointer to an I2C response message typedef.
483+
*/
484+
/******************************************************************************************/
485+
void publishI2CResponse(wippersnapper_signal_v1_I2CResponse *msgi2cResponse) {
486+
size_t msgSz;
487+
pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_I2CResponse_fields,
488+
msgi2cResponse);
489+
WS_DEBUG_PRINT("Publishing Message: I2CResponse...");
490+
WS._mqtt->publish(WS._topic_signal_i2c_device, WS._buffer_outgoing, msgSz, 1);
491+
WS_DEBUG_PRINTLN("Published!");
492+
}
493+
494+
/******************************************************************************************/
495+
/*!
496+
@brief Encodes an wippersnapper_signal_v1_I2CResponse message.
497+
@param msgi2cResponse
498+
A pointer to an wippersnapper_signal_v1_I2CResponse.
499+
@return True if encoded successfully, False otherwise.
500+
*/
501+
/******************************************************************************************/
502+
bool encodeI2CResponse(wippersnapper_signal_v1_I2CResponse *msgi2cResponse) {
503+
memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing));
504+
pb_ostream_t ostream =
505+
pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing));
506+
if (!pb_encode(&ostream, wippersnapper_signal_v1_I2CResponse_fields,
507+
msgi2cResponse)) {
508+
WS_DEBUG_PRINTLN("ERROR: Unable to encode I2C response message!");
509+
return false;
510+
}
511+
return true;
512+
}
513+
514+
/******************************************************************************************/
515+
/*!
516+
@brief Decodes an I2C signal request message and executes the
517+
callback based on the message's tag. If successful,
518+
publishes an I2C signal response back to the broker.
519+
@param stream
520+
Incoming data stream from buffer.
521+
@param field
522+
Protobuf message's tag type.
523+
@param arg
524+
Optional arguments from decoder calling function.
525+
@returns True if decoded successfully, False otherwise.
526+
*/
527+
/******************************************************************************************/
528+
bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field,
529+
void **arg) {
530+
bool is_success = true;
531+
WS_DEBUG_PRINTLN("cbDecodeSignalRequestI2C");
532+
// Create I2C Response
533+
wippersnapper_signal_v1_I2CResponse msgi2cResponse =
534+
wippersnapper_signal_v1_I2CResponse_init_zero;
535+
if (field->tag == wippersnapper_signal_v1_I2CRequest_req_i2c_init_tag) {
536+
WS_DEBUG_PRINTLN("I2C Init Request Found!");
537+
// Decode I2CInitRequest
538+
wippersnapper_i2c_v1_I2CInitRequest msgI2cInitRequest =
539+
wippersnapper_i2c_v1_I2CInitRequest_init_zero;
540+
if (!pb_decode(stream, wippersnapper_i2c_v1_I2CInitRequest_fields,
541+
&msgI2cInitRequest)) {
542+
WS_DEBUG_PRINTLN(
543+
"ERROR: Could not decode wippersnapper_i2c_v1_I2CInitRequest");
544+
return false; // fail out
545+
}
546+
547+
// Create a new I2C Component
548+
if (msgI2cInitRequest.i2c_port_number == 0) {
549+
WS._i2cPort0 = new WipperSnapper_Component_I2C(&msgI2cInitRequest);
550+
WS.i2cComponents.push_back(WS._i2cPort0);
551+
// did we init. the port successfully?
552+
is_success = WS._i2cPort0->isInitialized();
553+
} else if (msgI2cInitRequest.i2c_port_number == 1) {
554+
WS._i2cPort1 = new WipperSnapper_Component_I2C(&msgI2cInitRequest);
555+
// did we init. the port successfully?
556+
is_success = WS._i2cPort1->isInitialized();
557+
WS.i2cComponents.push_back(WS._i2cPort1);
558+
} else {
559+
WS_DEBUG_PRINTLN("ERROR: Both I2C ports are in-use");
560+
is_success = false;
561+
}
562+
563+
// Pack I2CResponse message
564+
msgi2cResponse.which_payload =
565+
wippersnapper_signal_v1_I2CRequest_req_i2c_init_tag;
566+
msgi2cResponse.payload.resp_i2c_init.is_initialized = is_success;
567+
568+
// Encode I2CResponse message
569+
if (!encodeI2CResponse(&msgi2cResponse)) {
570+
return false;
571+
}
572+
} else if (field->tag ==
573+
wippersnapper_signal_v1_I2CRequest_req_i2c_scan_tag) {
574+
WS_DEBUG_PRINTLN("I2C Scan Request");
575+
576+
// Decode I2CScanRequest
577+
wippersnapper_i2c_v1_I2CScanRequest msgScanReq =
578+
wippersnapper_i2c_v1_I2CScanRequest_init_zero;
579+
if (!pb_decode(stream, wippersnapper_i2c_v1_I2CScanRequest_fields,
580+
&msgScanReq)) {
581+
WS_DEBUG_PRINTLN(
582+
"ERROR: Could not decode wippersnapper_i2c_v1_I2CScanRequest");
583+
return false; // fail out if we can't decode the request
584+
}
585+
586+
// Check if I2C components were previously initialized
587+
if (!WS._i2cPort0->isInitialized() && !WS._i2cPort0->isInitialized()) {
588+
WS_DEBUG_PRINTLN(
589+
"ERROR: I2C Ports were not initialized prior to scanning!");
590+
return false;
591+
}
592+
593+
// Perform I2C scan
594+
wippersnapper_i2c_v1_I2CScanResponse scanResp =
595+
wippersnapper_i2c_v1_I2CScanResponse_init_zero;
596+
if (msgScanReq.i2c_port_number == 0) {
597+
scanResp = WS._i2cPort0->scanAddresses();
598+
} else {
599+
scanResp = WS._i2cPort1->scanAddresses();
600+
}
601+
WS_DEBUG_PRINTLN("Scan Complete!");
602+
WS_DEBUG_PRINT("\t# of addresses found on bus: ");
603+
WS_DEBUG_PRINTLN(scanResp.addresses_found_count);
604+
605+
// Pack I2CResponse
606+
msgi2cResponse.which_payload =
607+
wippersnapper_signal_v1_I2CResponse_resp_i2c_scan_tag;
608+
memcpy(msgi2cResponse.payload.resp_i2c_scan.addresses_found,
609+
scanResp.addresses_found, sizeof(scanResp.addresses_found));
610+
msgi2cResponse.payload.resp_i2c_scan.addresses_found_count =
611+
scanResp.addresses_found_count;
612+
613+
// Encode I2C Response
614+
if (!encodeI2CResponse(&msgi2cResponse)) {
615+
return false;
616+
}
617+
} else if (field->tag ==
618+
wippersnapper_signal_v1_I2CRequest_req_i2c_device_init_tag) {
619+
WS_DEBUG_PRINTLN("I2C Device Init Request Found!");
620+
// Decode stream into an I2CDeviceInitRequest
621+
wippersnapper_i2c_v1_I2CDeviceInitRequest msgI2CDeviceInitRequest =
622+
wippersnapper_i2c_v1_I2CDeviceInitRequest_init_zero;
623+
// Decode stream into struct, msgI2CDeviceInitRequest
624+
if (!pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequest_fields,
625+
&msgI2CDeviceInitRequest)) {
626+
WS_DEBUG_PRINTLN("ERROR: Could not decode I2CDeviceInitRequest message.");
627+
return false; // fail out if we can't decode
628+
}
629+
// Attach device to I2C port
630+
bool deviceInitSuccess = false;
631+
if (msgI2CDeviceInitRequest.i2c_port_number == 0 &&
632+
WS._i2cPort0->isInitialized() == true) {
633+
deviceInitSuccess =
634+
WS._i2cPort0->attachI2CDevice(&msgI2CDeviceInitRequest);
635+
} else if (msgI2CDeviceInitRequest.i2c_port_number == 1 &&
636+
WS._i2cPort1->isInitialized() == true) {
637+
deviceInitSuccess =
638+
WS._i2cPort1->attachI2CDevice(&msgI2CDeviceInitRequest);
639+
}
640+
// Create response
641+
msgi2cResponse = wippersnapper_signal_v1_I2CResponse_init_zero;
642+
msgi2cResponse.which_payload =
643+
wippersnapper_signal_v1_I2CResponse_resp_i2c_device_init_tag;
644+
msgi2cResponse.payload.resp_i2c_device_init.is_success = deviceInitSuccess;
645+
// Encode message
646+
memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing));
647+
pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing,
648+
sizeof(WS._buffer_outgoing));
649+
if (!pb_encode(&ostream, wippersnapper_signal_v1_I2CResponse_fields,
650+
&msgi2cResponse)) {
651+
WS_DEBUG_PRINTLN("ERROR: Unable to encode I2C response message");
652+
return false; // fail out if we cant encode
653+
}
654+
} else {
655+
WS_DEBUG_PRINTLN("ERROR: Undefined I2C message tag");
656+
return false; // fail out, we didn't encode anything to publish
657+
}
658+
publishI2CResponse(&msgi2cResponse);
659+
return is_success;
660+
}
661+
662+
/**************************************************************************/
663+
/*!
664+
@brief Called when i2c signal sub-topic receives a new message and
665+
attempts to decode a signal request message.
666+
@param data
667+
Incoming data from MQTT broker.
668+
@param len
669+
Length of incoming data.
670+
*/
671+
/**************************************************************************/
672+
void cbSignalI2CReq(char *data, uint16_t len) {
673+
WS_DEBUG_PRINTLN("* NEW MESSAGE [Topic: Signal-I2C]: ");
674+
WS_DEBUG_PRINT(len);
675+
WS_DEBUG_PRINTLN(" bytes.");
676+
// zero-out current buffer
677+
memset(WS._buffer, 0, sizeof(WS._buffer));
678+
// copy mqtt data into buffer
679+
memcpy(WS._buffer, data, len);
680+
WS.bufSize = len;
681+
682+
// Zero-out existing I2C signal msg.
683+
WS.msgSignalI2C = wippersnapper_signal_v1_I2CRequest_init_zero;
684+
685+
// Set up the payload callback, which will set up the callbacks for
686+
// each oneof payload field once the field tag is known
687+
WS.msgSignalI2C.cb_payload.funcs.decode = cbDecodeSignalRequestI2C;
688+
689+
// Decode I2C signal request
690+
pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize);
691+
if (!pb_decode(&istream, wippersnapper_signal_v1_I2CRequest_fields,
692+
&WS.msgSignalI2C))
693+
WS_DEBUG_PRINTLN("ERROR: Unable to decode I2C message");
694+
}
695+
479696
/****************************************************************************/
480697
/*!
481698
@brief Handles MQTT messages on signal topic until timeout.
@@ -781,6 +998,18 @@ bool Wippersnapper::buildWSTopics() {
781998
sizeof(char) * strlen(WS._username) + strlen("/wprsnpr/") +
782999
strlen(_device_uid) + strlen(TOPIC_SIGNALS) + strlen("broker") + 1);
7831000

1001+
// Topic for i2c signals from device to broker
1002+
WS._topic_signal_i2c_brkr = (char *)malloc(
1003+
sizeof(char) * strlen(WS._username) + +strlen("/") + strlen(_device_uid) +
1004+
strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) + strlen("broker") +
1005+
strlen(TOPIC_I2C) + 1);
1006+
1007+
// Topic for i2c signals from broker to device
1008+
WS._topic_signal_i2c_device = (char *)malloc(
1009+
sizeof(char) * strlen(WS._username) + +strlen("/") + strlen(_device_uid) +
1010+
strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) + strlen("device") +
1011+
strlen(TOPIC_I2C) + 1);
1012+
7841013
// Create global registration topic
7851014
if (WS._topic_description) {
7861015
strcpy(WS._topic_description, WS._username);
@@ -854,6 +1083,32 @@ bool Wippersnapper::buildWSTopics() {
8541083
is_success = false;
8551084
}
8561085

1086+
// Create device-to-broker i2c signal topic
1087+
if (WS._topic_signal_i2c_brkr) {
1088+
strcpy(WS._topic_signal_i2c_brkr, WS._username);
1089+
strcat(WS._topic_signal_i2c_brkr, TOPIC_WS);
1090+
strcat(WS._topic_signal_i2c_brkr, _device_uid);
1091+
strcat(WS._topic_signal_i2c_brkr, TOPIC_SIGNALS);
1092+
strcat(WS._topic_signal_i2c_brkr, "broker");
1093+
strcat(WS._topic_signal_i2c_brkr, TOPIC_I2C);
1094+
} else { // malloc failed
1095+
WS._topic_signal_i2c_brkr = 0;
1096+
is_success = false;
1097+
}
1098+
1099+
// Create broker-to-device i2c signal topic
1100+
if (WS._topic_signal_i2c_device) {
1101+
strcpy(WS._topic_signal_i2c_device, WS._username);
1102+
strcat(WS._topic_signal_i2c_device, TOPIC_WS);
1103+
strcat(WS._topic_signal_i2c_device, _device_uid);
1104+
strcat(WS._topic_signal_i2c_device, TOPIC_SIGNALS);
1105+
strcat(WS._topic_signal_i2c_device, "device");
1106+
strcat(WS._topic_signal_i2c_device, TOPIC_I2C);
1107+
} else { // malloc failed
1108+
WS._topic_signal_i2c_device = 0;
1109+
is_success = false;
1110+
}
1111+
8571112
return is_success;
8581113
}
8591114

@@ -869,6 +1124,12 @@ void Wippersnapper::subscribeWSTopics() {
8691124
WS._mqtt->subscribe(_topic_signal_brkr_sub);
8701125
_topic_signal_brkr_sub->setCallback(cbSignalTopic);
8711126

1127+
// Subscribe to signal's I2C sub-topic
1128+
_topic_signal_i2c_sub =
1129+
new Adafruit_MQTT_Subscribe(WS._mqtt, WS._topic_signal_i2c_brkr, 1);
1130+
WS._mqtt->subscribe(_topic_signal_i2c_sub);
1131+
_topic_signal_i2c_sub->setCallback(cbSignalI2CReq);
1132+
8721133
// Subscribe to registration status topic
8731134
_topic_description_sub =
8741135
new Adafruit_MQTT_Subscribe(WS._mqtt, WS._topic_description_status, 1);
@@ -1231,6 +1492,7 @@ ws_status_t Wippersnapper::run() {
12311492
WS._mqtt->processPackets(10);
12321493
feedWDT();
12331494

1495+
// TODO: Loop thru components
12341496
// Process digital inputs, digitalGPIO module
12351497
WS._digitalGPIO->processDigitalInputs();
12361498
feedWDT();
@@ -1240,4 +1502,4 @@ ws_status_t Wippersnapper::run() {
12401502
feedWDT();
12411503

12421504
return WS_NET_CONNECTED; // TODO: Make this funcn void!
1243-
}
1505+
}

0 commit comments

Comments
 (0)