Im using an arduino nano 33 ble sense on the ArduinoCore-mbed boards package, and using the library Wire.h inside of this to communicate over i2c as a slave. Im trying to talk to a wiimote, and have hooked everything, including my two 4.7k pullup resistors.
The wiimote should ask for two bytes at 0xFE, and then write me a response back. When i ran my program, i got no response back:
16:13:44.244 -> Ardwiinote starting...
16:13:44.244 -> Starting device at 0x53
16:13:57.752 ->
16:13:57.752 -> Recieving information...
16:13:57.892 ->
16:13:57.892 -> Handling request to register 0xFE with offset 0x0
16:13:57.985 -> Sending bytes: 0x0,0x5
I am assuming the reason for the hang is that i had not implemented a way to ack/nack messages. But after looking through Wire.h, i had only found twi_reply(1); which is not available on my arm board. Im not sure if Wire.h ack's automatically and the cause is elsewhere, or if it doesn't and i have to find a workaround. Here is my code:
#include <Wire.h>
WiimoteEmu::MotionPlus motion_plus;
std::uint8_t current_address = motion_plus.INACTIVE_DEVICE_ADDR;
static std::uint8_t state = 0;
#define SERIAL_BAUD 115200
#define MAX_REQUEST_SIZE 32
#define CLOCK_FREQ 400000
static void receive_bytes(int count) {
Serial.println("\nRecieving information...");
delay(100);
// Determine if recived bytes were for request or write
// Depending on the amount of bytes sent
// First byte recieved is ALWAYS address
state = Wire.read();
if (count > 1) {
std::uint8_t* buffer;
Serial.print("Recieved bytes to register 0x");
Serial.println(state, HEX);
delay(100);
Serial.print("\tRecieved bytes: ");
for (int i = 1; i < count; i++) {
buffer[i-1] = Wire.read();
Serial.print(i == 1 ? "0x" : ",0x");
Serial.print(buffer[i-1], HEX);
delay(50);
}
motion_plus.BusWrite(current_address, state, count-1, buffer);
}
}
static void handle_request() {
static std::uint8_t last_state = 0xFF;
static int offset = 0; // apparently offset starts at 0x08? im not sure if this is true
std::uint8_t buffer[MAX_REQUEST_SIZE] = {0xFF};
// Used to update offset if requested register is same
if(last_state == state) offset += MAX_REQUEST_SIZE;
else {
last_state = state;
offset = 0;
}
// Updates values in buffer to represent data at requested register
int buffer_len = motion_plus.BusRead(current_address, state+offset, MAX_REQUEST_SIZE, buffer);
Serial.print("\nHandling request to register 0x");
Serial.print(state, HEX);
Serial.print(" with offset 0x");
Serial.println(offset, HEX);
delay(100);
if(buffer_len > 0){
Serial.print("\tSending bytes: ");
for(int i = 0; i < buffer_len; i++){
Serial.print(i == 0 ? "0x" : ",0x");
Serial.print(buffer[i], HEX);
delay(50);
}
// Write data to I2C Master
Wire.write(buffer, buffer_len);
}
else Serial.println("\tNo more bytes available for that que!");
}
void StartI2C(std::uint8_t addr){
// Check if we should be starting at a new address or changing an old one
if (addr != current_address){
current_address = addr;
Wire.end();
}
// Begins I2C communication at address
Wire.begin(current_address);
Wire.setClock(CLOCK_FREQ);
// Sets up handlers to BusRead/BusWrite functions
Wire.onReceive(receive_bytes);
Wire.onRequest(handle_request);
Serial.print("Starting device at 0x");
Serial.println(current_address, HEX);
}
void setup() {
// put your setup code here, to run once:
// You'll need to dispatch ALL i2c reads and writes to the MotionPlus object.
// Resets all state
motion_plus.Reset();
// Begins sensor data collection
Magnetic.begin();
Gyroscope.begin();
Accelerometer.begin();
// Starts serial communication
Serial.begin(SERIAL_BAUD);
while(!Serial);
Serial.println("Ardwiinote starting...");
// Begins I2C communication at inactive address
StartI2C(current_address);
delay(5000);
}
void UpdateSlaveAddress(){
// after an Update() call the motion plus might decide on a different value
// for its device detect pin. it pretends to the wii remote to disconnect and
// connect on initialization. this is required for normal operation. You will
// need to set a pin depending on this value. I can't remember if it's active
// high or low and I assume there is a pull-up/down on the wii remote end.
// for now, this function simply changes the slave address to an active one.
if(motion_plus.ReadDeviceDetectPin() && current_address == motion_plus.INACTIVE_DEVICE_ADDR){
StartI2C(motion_plus.ACTIVE_DEVICE_ADDR);
}
else if (!motion_plus.ReadDeviceDetectPin() && current_address == motion_plus.ACTIVE_DEVICE_ADDR){
StartI2C(motion_plus.INACTIVE_DEVICE_ADDR);
}
}
void UpdateGyro(){
// Gather multiple sources of data for sensor fusion
Nano33BLEMagneticData magneticData;
Nano33BLEGyroscopeData gyroscopeData;
Nano33BLEAccelerometerData accelerometerData;
// Checks if data was gathered succesfully
if(Magnetic.pop(magneticData),
Gyroscope.pop(gyroscopeData),
Accelerometer.pop(accelerometerData)){
// Update quaternion values by giving the sensor fusion algrithom data
MadgwickAHRSupdate(deg2rad(gyroscopeData.x), deg2rad(gyroscopeData.y), deg2rad(gyroscopeData.z),
accelerometerData.x, accelerometerData.y, accelerometerData.z,
magneticData.x, magneticData.y, magneticData.z);
// Ideally, to simulate the actual hardware, this is triggered on a read of a
// specific i2c area when the M+ is in a certain state. The interface is a bit
// different because dolphin's emulation is a bit higher level here.
motion_plus.PrepareInput(q0, q1, q2, q3);
}
else Serial.println("\nWARNING: ERROR OCCURED WHILE GRABBING SENSOR DATA\n");
}
void loop() {
// put your main code here, to run repeatedly:
// The MotionPlus object expects to have its Update function called
// UPDATE_FREQ times a second (200hz). It has some internal "timers".
motion_plus.Update();
UpdateSlaveAddress();
if(current_address == motion_plus.ACTIVE_DEVICE_ADDR) UpdateGyro();
}