How can I transfer snapshot data using I2C instead of going through WebUSB? #35
-
Beta Was this translation helpful? Give feedback.
Replies: 0 comments 10 replies
-
|
The code is pretty specific to the used cellular module (a Blues Notecard), which uses JSON via I2C for communication. This is probably quite different from what you would want to do when talking to your ESP32, but below is a little library for reference. (The acelerometer-snappergps firmware is another example that includes I2C communication.) /****************************************************************************
* i2c.c
* SnapperGPS
* Jonas Beuchert
* 2022
* Functions to communicate with Notecard via I2C
*****************************************************************************/
#include "em_cmu.h"
#include "em_emu.h"
#include "em_i2c.h"
#include "em_gpio.h"
#include "pinouts.h" // For debugging only
#include "timer.h" // For debugging only
#include "notecard_i2c.h"
// I2C variables
static volatile I2C_TransferReturn_TypeDef I2C_Status;
// Interrupt handler
void I2C0_IRQHandler(void) {
I2C_Status = I2C_Transfer(I2C0);
}
// Public functions
void I2C_enableInterface() {
CMU_ClockEnable(cmuClock_I2C0, true);
GPIO_PinModeSet(I2C_SDA_PORT, I2C_SDA_PIN, gpioModeWiredAnd, 1);
GPIO_PinModeSet(I2C_SCL_PORT, I2C_SCL_PIN, gpioModeWiredAnd, 1);
for (int i = 0; i < 9; i++) {
GPIO_PinModeSet(I2C_SCL_PORT, I2C_SCL_PIN, gpioModeWiredAnd, 0);
GPIO_PinModeSet(I2C_SCL_PORT, I2C_SCL_PIN, gpioModeWiredAnd, 1);
}
I2C0->ROUTE = I2C_ROUTE_SDAPEN | I2C_ROUTE_SCLPEN | I2C_LOC;
I2C_Init_TypeDef i2cInit = I2C_INIT_DEFAULT;
I2C_Init(I2C0, &i2cInit);
NVIC_ClearPendingIRQ(I2C0_IRQn);
NVIC_EnableIRQ(I2C0_IRQn);
}
void I2C_disableInterface() {
GPIO_PinModeSet(I2C_SDA_PORT, I2C_SDA_PIN, gpioModeDisabled, 0);
GPIO_PinModeSet(I2C_SCL_PORT, I2C_SCL_PIN, gpioModeDisabled, 0);
I2C_Reset(I2C0);
CMU_ClockEnable(cmuClock_I2C0, false);
NVIC_DisableIRQ(I2C0_IRQn);
}
/**************************************************************************/
/*!
@brief Transmits an amount of data from the host in blocking mode.
@param[in] device_address
The I2C address.
@param[in] buffer
The data to transmit over I2C. The caller should have shifted
it right so that the low bit is NOT the read/write bit.
@param[in] size
The number of bytes to transmit.
@returns A string with an error, or `nullptr` if the transmission was
successful.
*/
/**************************************************************************/
const char *noteI2cTransmit(uint16_t device_address_, uint8_t *buffer_, uint16_t size_) {
// Adapted from AN0011
I2C_TransferSeq_TypeDef seq;
seq.addr = device_address_;
seq.flags = I2C_FLAG_WRITE;
seq.buf[0].data = buffer_;
seq.buf[0].len = size_;
// Do a polled transfer
I2C_Status = I2C_TransferInit(I2C0, &seq);
while (I2C_Status == i2cTransferInProgress)
{
// Enter EM1 while waiting for I2C interrupt
EMU_EnterEM1();
// Could do a timeout function here
}
const char * result = NULL;
return result;
}
/**************************************************************************/
/*!
@brief Receives an amount of data from the Notecard in blocking mode.
@param[in] device_address
The I2C address.
@param[out] buffer
A buffer to hold the data read from the I2C controller.
@param[in] requested_byte_count
The number of bytes requested.
@param[out] available
The number of bytes available for subsequent calls to receive().
@returns A string with an error, or `nullptr` if the receive was
successful.
*/
/**************************************************************************/
const char *noteI2cReceive(uint16_t device_address_, uint8_t *buffer_, uint16_t size_, uint32_t *available_) {
// Adapted from AN0011
I2C_TransferSeq_TypeDef seq;
seq.addr = device_address_;
seq.flags = I2C_FLAG_READ;
seq.buf[0].data = buffer_;
seq.buf[0].len = size_; // Might need to add REQUEST_HEADER_SIZE = 2
// Do a polled transfer
I2C_Status = I2C_TransferInit(I2C0, &seq);
while (I2C_Status == i2cTransferInProgress)
{
// Enter EM1 while waiting for I2C interrupt
EMU_EnterEM1();
// Could do a timeout function here
}
const char * result = NULL;
return result;
// TODO: set available
}
static const char basis_64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int JB64EncodeLen(int len)
{
return ((len + 2) / 3 * 4) + 1;
}
int JB64Encode(char *encoded, const char *string, int len)
{
int i;
char *p;
p = encoded;
for (i = 0; i < len - 2; i += 3) {
*p++ = basis_64[(string[i] >> 2) & 0x3F];
*p++ = basis_64[((string[i] & 0x3) << 4) | ((int) (string[i + 1] & 0xF0) >> 4)];
*p++ = basis_64[((string[i + 1] & 0xF) << 2) | ((int) (string[i + 2] & 0xC0) >> 6)];
*p++ = basis_64[string[i + 2] & 0x3F];
}
if (i < len) {
*p++ = basis_64[(string[i] >> 2) & 0x3F];
if (i == (len - 1)) {
*p++ = basis_64[((string[i] & 0x3) << 4)];
*p++ = '=';
} else {
*p++ = basis_64[((string[i] & 0x3) << 4) | ((int) (string[i + 1] & 0xF0) >> 4)];
*p++ = basis_64[((string[i + 1] & 0xF) << 2)];
}
*p++ = '=';
}
*p++ = '\0';
return p - encoded;
}This allows me to send JSON data to the cellular module using their specific JSON commands. For binary data, like the raw snapshots, I use base64 encoding (example below), something you probably would not want to do when writing your own comms protocol. char* snapshotAddress = (char*)SNAPSHOT_BUFFER_LOCATION;
int binaryDataLen = 48;
memset(message, 0, 255);
for (size_t toTransmitLen = SNAPSHOT_BUFFER_SIZE; toTransmitLen > 0; toTransmitLen -= (size_t) binaryDataLen) {
JB64Encode(&message[1], snapshotAddress, binaryDataLen);
msgLen = strlen(&message[1]);
((uint8_t *) message)[0] = (uint8_t) msgLen;
noteI2cTransmit(i2caddress, (uint8_t *) message, msgLen + 1);
Timer_delayMilliseconds(1);
snapshotAddress += binaryDataLen * sizeof(uint8_t);
}P.S: If you end up building something cool, I am of course happy to see it :) But no pressure. |
Beta Was this translation helpful? Give feedback.

Oh, OK, let me give a few more details.
You want to look in the firmware and find the lines where you turn the radio off after acquiring a snapshot, these lines:
After these lines, you insert the new code.
First, enable I2C:
I2C_enableInterface();Just as an example, sending some text via I2C works like this:
H…