Skip to content
42 changes: 42 additions & 0 deletions src/SerialSolver_i2c.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* @file SerialSolver_i2c.cpp
* @brief I2C command handling for DEMIR controller
* @author Muhammet Şam Rossiter
* @date 2025
*/

#include "SerialSolver.h"
#include "motor.h"
#include "controller.h"

void SerialSolver::proccessI2CCommand(uint8_t* data, uint8_t length) {
if (length < 2) return;

uint8_t motorIndex = data[0];
uint8_t command = data[1];
int16_t param = 0;

if (length >= 4) {
param = (data[2] << 8) | data[3];
}

switch (command) {
case 0x01: // set motor speed
Controller.set_uManuel(param, motorIndex);
break;
case 0x02: // enable motor
Motor.enable(motorIndex);
break;
case 0x03: // disable motor
Motor.disable(motorIndex);
break;
case 0x04: // reset encoder
Motor.resetPosition(motorIndex);
break;
case 0x05: // set position reference
Controller.setPositionReference(param, motorIndex);
break;
default:
break;
}
}
14 changes: 14 additions & 0 deletions src/conf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef CONF_H
#define CONF_H

// Serial configuration
#define BAUDRATE 115200
#define TIMEOUT 100 // Serial timeout in ms

// I2C configuration -
#define MOTOR_CONTROLLER_ADDR_1 0x08

// Control loop timing
#define LOOP_RATE_MS 3 // Main control loop rate

#endif // CONF_H
55 changes: 55 additions & 0 deletions src/i2c_comm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @file i2c_comm.cpp
* @brief I2C communication interface implementation for DEMIR controller
* @author Şam Rossiter
* @date 2025
* @details Implements I2C master/slave routines for sending commands to
* the DEMIR motor controller. References controller I2C addresses
* defined in conf.h
*/

#include <Wire.h>
#include "i2c_comm.h"
#include "conf.h" // for I2C addresses
#include "controller.h" // for Solver
#include "motor.h" // for Motor

uint8_t i2cCmdBuffer[I2C_CMD_BUF_SIZE];
uint8_t i2cCmdIndex = 0;

/**
* @brief I2C receive event callback
* @param byteCount Number of bytes received
* @details Called when I2C master writes data to this controller.
* Buffers incoming command and triggers Solver for processing.
*/
void receiveI2CCommand(int byteCount) {
while (Wire.available() && i2cCmdIndex < I2C_CMD_BUF_SIZE) {
i2cCmdBuffer[i2cCmdIndex++] = Wire.read();
}
if (i2cCmdIndex > 0) {
Solver.proccessI2CCommand(i2cCmdBuffer, i2cCmdIndex);
i2cCmdIndex = 0;
}
}

/**
* @brief I2C request event callback
* @details Called when I2C master requests data from this controller.
* Sends the current motor position (MOTOR_1) as a 4-byte response.
*/
void sendI2CResponse() {
uint32_t pos = Motor.getPosition(MOTOR_1);
Wire.write((uint8_t*)&pos, sizeof(pos));
}

/**
* @brief Initialize I2C interface
* @param slaveAddress I2C address of this controller
* @details Configures Wire library in slave mode and attaches receive/request callbacks.
*/
void initI2C(uint8_t slaveAddress) {
Wire.begin(slaveAddress); // join I2C bus with defined address
Wire.onReceive(receiveI2CCommand); // attach receive handler
Wire.onRequest(sendI2CResponse); // attach request handler
}
22 changes: 22 additions & 0 deletions src/i2c_comm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* @file i2c_comm.h
* @brief I2C communication interface for DEMIR motor controller
* @author Sam Rossiter
* @date 2025
* @details Handles receiving commands and sending responses over I2C.
*/

#pragma once
#include <Wire.h>
#include "controller.h"
#include "motor.h"
#include "SerialSolver.h"

#define DEMIR_I2C_ADDR 0x10
#define I2C_CMD_BUF_SIZE 8

extern uint8_t i2cCmdBuffer[I2C_CMD_BUF_SIZE];
extern uint8_t i2cCmdIndex;

void receiveI2CCommand(int byteCount);
void sendI2CResponse();
107 changes: 44 additions & 63 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/**
* @file main.cpp
* @brief Main application entry point for DEMIR motor controller
* @brief Main application entry point for DEMIR motor controller with I2C support
* @author Muhammet Şükrü Demir
* @date 2020
* @version 1.1.1
* @details This is the main application file that initializes the DEMIR motor control
* system and implements the main control loop with serial command processing,
* motor control execution, and data logging capabilities.
* @date 2025
* @version 1.1.2
* @details Initializes DEMIR motor control system, implements main control loop,
* handles serial and I2C commands, motor control execution, and data logging.
*/

#include <Arduino.h>
#include <Wire.h>

#include "Motion.h"
#include "MotorDriver.h"
Expand All @@ -18,84 +18,67 @@
#include "com_def.h"
#include "conf.h"
#include "controller.h"
#include "i2c_comm.h" // I2C interface header

/// @brief Main control loop rate in milliseconds
uint8_t loopRate = 3;

/**
* @brief Arduino setup function - system initialization
* @details Initializes serial communication, motor drivers, and status LEDs.
* Performs LED test sequence to verify hardware functionality.
*/
void setup() {
// Initialize serial communication
Serial.begin( BAUDRATE );
while ( !Serial ) {
// Wait for serial port to initialize
}
Serial.setTimeout( TIMEOUT );
Serial.begin(BAUDRATE);
while (!Serial) {}
Serial.setTimeout(TIMEOUT);

Serial.println(F("DEMIR v1.1.2"));

// Print system identification
Serial.println( F( "DEMIR v1.1.1" ) );

// Initialize motor driver subsystem
Driver.init();
Serial.println( F( "ok!" ) );
Serial.println(F("ok!"));

// Initialize status LEDs
pinMode( STATUS_LED_BLUE, OUTPUT );
pinMode( STATUS_LED_RED, OUTPUT );
pinMode(STATUS_LED_BLUE, OUTPUT);
pinMode(STATUS_LED_RED, OUTPUT);

// LED startup test sequence
digitalWrite( STATUS_LED_BLUE, HIGH );
delay( 300 );
digitalWrite( STATUS_LED_BLUE, LOW );
digitalWrite(STATUS_LED_BLUE, HIGH);
delay(300);
digitalWrite(STATUS_LED_BLUE, LOW);

digitalWrite( STATUS_LED_RED, HIGH );
delay( 300 );
digitalWrite( STATUS_LED_RED, LOW );
digitalWrite(STATUS_LED_RED, HIGH);
delay(300);
digitalWrite(STATUS_LED_RED, LOW);

// Initialize I2C interface
Wire.begin(DEMIR_I2C_ADDR);
Wire.onReceive(receiveI2CCommand);
Wire.onRequest(sendI2CResponse);
}

/**
* @brief Arduino main loop function
* @details Implements the main control loop with timing control, motor driver execution,
* data logging, and serial command processing. Runs continuously at specified
* loop rate for real-time motor control.
*/
void loop() {
// Update timing for control loop
Controller.currentTime = millis(); ///< Current timestamp
Controller.deltaT
= Controller.currentTime - Controller.oldTime; ///< Time since last control update

// Execute control loop at specified rate
if ( Controller.deltaT >= loopRate ) // Execute when deltaT exceeds loopRate
{
Controller.currentTime = millis();
Controller.deltaT = Controller.currentTime - Controller.oldTime;

if (Controller.deltaT >= loopRate) {
Controller.oldTime = Controller.currentTime;

// Execute motor control algorithms
Driver.run();

// Handle data logging if enabled
if ( Solver.logEnable ) // Check if logging is enabled
{
switch ( Solver.logWhat ) // Determine what to log
{
if (Solver.logEnable) {
switch (Solver.logWhat) {
case LOG_POSITION:
/// Log current motor position
Solver.logForMatlab[ Solver.logCounter ] = ( unsigned long ) Motor.getPosition( MOTOR_1 );
Solver.logForMatlab[Solver.logCounter] = (unsigned long)Motor.getPosition(MOTOR_1);
break;
case LOG_CURRENT:
/// Log current sensor data (not implemented)
// unsigned long tempHex = *( unsigned long* ) &totalCurrent;
// logForMatlab[ Solver.logCounter ] = tempHex;
break;
}

// Check if log buffer is full
if ( ++Solver.logCounter == Solver.logSize ) {
Solver.logEnable = false; // Disable logging
Solver.logCounter = 0; // Reset counter

if (++Solver.logCounter == Solver.logSize) {
Solver.logEnable = false;
Solver.logCounter = 0;
}
}
}
Expand All @@ -104,15 +87,13 @@ void loop() {
Solver.proccessCommands();

// Send logged data to MATLAB if requested
if ( Solver.sendLogToMatlab ) {
if (Solver.sendLogToMatlab) {
Solver.sendLogToMatlab = false;

// Transmit logged data as 4-byte values
for ( uint16_t j = 0; j < Solver.logSize; j++ ) {
Serial.write( Solver.logForMatlab[ j ] ); ///< LSB
Serial.write( Solver.logForMatlab[ j ] >> 8 ); ///< Byte 1
Serial.write( Solver.logForMatlab[ j ] >> 16 ); ///< Byte 2
Serial.write( Solver.logForMatlab[ j ] >> 24 ); ///< MSB
for (uint16_t j = 0; j < Solver.logSize; j++) {
Serial.write(Solver.logForMatlab[j]);
Serial.write(Solver.logForMatlab[j] >> 8);
Serial.write(Solver.logForMatlab[j] >> 16);
Serial.write(Solver.logForMatlab[j] >> 24);
}
}
}