AccelStepperI2C  v0.2.2
I2C wrapper (and a bit more) for the AccelStepper Arduino library
firmware.ino File Reference

Firmware for an I2C slave controlling up to 8 stepper motors with the AccelStepper library, comes with the matching AccelStepperI2C library. More...

Detailed Description

Firmware for an I2C slave controlling up to 8 stepper motors with the AccelStepper library, comes with the matching AccelStepperI2C library.

This documentation is probably only relevant for you if you want to change the firmware. Otherwise, the library documentation should suffice.

Author

Copyright (c) 2022 juh

License

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.

Todo:

return messages (results) should ideally come with an id, too, so that master can be sure it's the correct result. Currently only CRC8, i.e. correct transmission is checked.

volatile variables / ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {}

Module support (Servo, pin control, ...) needs to be outsourced and centralized. Currently there are a number of places that need to be adapted if a new module is added: Init section, startup message in setup(), processMessage().

make i2c address configurable with pins or via i2c/EEPROM

implement endstops/reference positions

Classes

struct  Endstop
 
struct  Stepper
 This struct comprises all stepper parameters needed for local slave management. More...
 

Macros

#define SERVO_SUPPORT
 
#define PINCONTROL_SUPPORT
 
#define DEBUG
 Uncomment this to enable time keeping diagnostics. You probably should disable debugging, as Serial output will distort the measurements severely. Diagnostics take a little extra time and ressources, so you best disable it in production environments. More...
 
#define log(...)   Serial.print(__VA_ARGS__)
 
#define EEPROM_OFFSET_I2C_ADDRESS   0
 

Functions

uint8_t retrieveI2C_address ()
 Read a stored I2C address from EEPROM. If there is none, use default. More...
 
void storeI2C_address (uint8_t newAddress)
 Write I2C address to EEPROM. More...
 
void resetFunc ()
 
void setup ()
 Setup system. Retrieve I2C address from EEPROM or default and initialize I2C slave. More...
 
int8_t addStepper (uint8_t interface=AccelStepper::FULL4WIRE, uint8_t pin1=2, uint8_t pin2=3, uint8_t pin3=4, uint8_t pin4=5, bool enable=true)
 Assign and initialize new stepper. Calls the AccelStepper's[1/2] constructor More...
 
uint8_t pollEndstops (uint8_t s)
 
void triggerInterrupt (uint8_t source, uint8_t reason)
 
void clearInterrupt ()
 
void loop ()
 Main loop, implements the state machine. Will check for each stepper's state and do the appropriate polling (run() etc.) as needed. Also checks for incoming commands and passes them on for processing. More...
 
void receiveEvent (int howMany)
 Handle I2C receive event. Just read the message and inform main loop. More...
 
bool validStepper (int8_t s)
 
bool validServo (int8_t s)
 
void processMessage (uint8_t len)
 
void writeOutputBuffer ()
 
void requestEvent ()
 Handle I2C request event. Will send results or information requested by the last command, as defined by the contents of the outputBuffer. More...
 

Variables

const uint8_t slaveDefaultAddress = 0x08
 
const uint8_t maxServos = 4
 
Servo servos [maxServos]
 
uint8_t numServos = 0
 
volatile uint8_t writtenToBuffer = 0
 
volatile uint8_t sentOnRequest = 0
 
uint32_t now
 
uint32_t then = millis()
 
bool reportNow = true
 
uint32_t lastCycles = 0
 
const uint32_t reportPeriod = 2000
 
uint32_t cycles = 0
 
const uint32_t eepromI2CaddressMarker = 0x12C0ACCF
 
SimpleBufferbufferIn
 
SimpleBufferbufferOut
 
volatile uint8_t newMessage = 0
 
int8_t interruptPin = -1
 
bool interruptActiveHigh = true
 
uint8_t interruptSource = 0xF
 
uint8_t interruptReason = interruptReason_none
 
const uint8_t maxEndstops = 2
 
const uint32_t endstopDebouncePeriod = 5
 
const uint8_t maxSteppers = 8
 
uint8_t numSteppers = 0
 
Stepper steppers [maxSteppers]
 

Macro Definition Documentation

◆ DEBUG

#define DEBUG

Uncomment this to enable time keeping diagnostics. You probably should disable debugging, as Serial output will distort the measurements severely. Diagnostics take a little extra time and ressources, so you best disable it in production environments.

Uncomment this to enable firmware debugging output on Serial.

◆ EEPROM_OFFSET_I2C_ADDRESS

#define EEPROM_OFFSET_I2C_ADDRESS   0

◆ log

#define log (   ...)    Serial.print(__VA_ARGS__)

◆ PINCONTROL_SUPPORT

#define PINCONTROL_SUPPORT

◆ SERVO_SUPPORT

#define SERVO_SUPPORT

Function Documentation

◆ addStepper()

int8_t addStepper ( uint8_t  interface = AccelStepper::FULL4WIRE,
uint8_t  pin1 = 2,
uint8_t  pin2 = 3,
uint8_t  pin3 = 4,
uint8_t  pin4 = 5,
bool  enable = true 
)

Assign and initialize new stepper. Calls the AccelStepper's[1/2] constructor

Returns
internal number (0-7) of stepper assigned to new stepper, -1 for error

◆ clearInterrupt()

void clearInterrupt ( )

◆ loop()

void loop ( )

Main loop, implements the state machine. Will check for each stepper's state and do the appropriate polling (run() etc.) as needed. Also checks for incoming commands and passes them on for processing.

Todo:
endstop polling: overhead to check if polling is needed (timeToCheckTheEndstops) might cost more than it saves, so maybe just check each cycle even if it might be much more often than needed.

◆ pollEndstops()

uint8_t pollEndstops ( uint8_t  s)

◆ processMessage()

void processMessage ( uint8_t  len)

@ brief Process the message stored in bufferIn with lenght len which was received via I2C. Messages consist of at least three bytes: [0] CRC8 checksum [1] Command. Most commands correspond to an AccelStepper or Servo function, some are new for I2C reasons. Unknown commands are ignored. [2] Number of the stepper or servo unit that is to receive the command. If no unit with this number exists, the message is ignored, except for general wrapper commands like resetCmd, which ignore this parameter. [3..maxBuffer] Optional parameter bytes. Each command that comes with too many or too few parameter bytes is ignored.

Note
Blocking functions runToPositionCmd() and runToNewPositionCmd() not implemented here. Blocking at the slave's side seems not too useful, as the master won't wait for us anyway,
Todo:
Implement blocking functions runToPositionCmd() and runToNewPositionCmd() on the master's side/in the library?

◆ receiveEvent()

void receiveEvent ( int  howMany)

Handle I2C receive event. Just read the message and inform main loop.

Todo:
Just read the buffer here, move interpretation out of the interrupt.

◆ requestEvent()

void requestEvent ( )

Handle I2C request event. Will send results or information requested by the last command, as defined by the contents of the outputBuffer.

Todo:
<done>Find sth. meaningful to report from requestEvent() if no message is pending, e.g. current position etc.</done> not implemented, ESP32 can't do that (doh)

◆ resetFunc()

void resetFunc ( )

◆ retrieveI2C_address()

uint8_t retrieveI2C_address ( )

Read a stored I2C address from EEPROM. If there is none, use default.

◆ setup()

void setup ( )

Setup system. Retrieve I2C address from EEPROM or default and initialize I2C slave.

◆ storeI2C_address()

void storeI2C_address ( uint8_t  newAddress)

Write I2C address to EEPROM.

Note
ESP eeprom.h works a bit different from AVRs: https://arduino-esp8266.readthedocs.io/en/3.0.2/libraries.html#eeprom

◆ triggerInterrupt()

void triggerInterrupt ( uint8_t  source,
uint8_t  reason 
)

◆ validServo()

bool validServo ( int8_t  s)

◆ validStepper()

bool validStepper ( int8_t  s)

◆ writeOutputBuffer()

void writeOutputBuffer ( )

Variable Documentation

◆ bufferIn

SimpleBuffer* bufferIn

◆ bufferOut

SimpleBuffer* bufferOut

◆ cycles

uint32_t cycles = 0

◆ eepromI2CaddressMarker

const uint32_t eepromI2CaddressMarker = 0x12C0ACCF

◆ endstopDebouncePeriod

const uint32_t endstopDebouncePeriod = 5

◆ interruptActiveHigh

bool interruptActiveHigh = true

◆ interruptPin

int8_t interruptPin = -1

◆ interruptReason

uint8_t interruptReason = interruptReason_none

◆ interruptSource

uint8_t interruptSource = 0xF

◆ lastCycles

uint32_t lastCycles = 0

◆ maxEndstops

const uint8_t maxEndstops = 2

◆ maxServos

const uint8_t maxServos = 4

◆ maxSteppers

const uint8_t maxSteppers = 8

◆ newMessage

volatile uint8_t newMessage = 0

◆ now

uint32_t now

◆ numServos

uint8_t numServos = 0

◆ numSteppers

uint8_t numSteppers = 0

◆ reportNow

bool reportNow = true

◆ reportPeriod

const uint32_t reportPeriod = 2000

◆ sentOnRequest

volatile uint8_t sentOnRequest = 0

◆ servos

Servo servos[maxServos]

◆ slaveDefaultAddress

const uint8_t slaveDefaultAddress = 0x08

◆ steppers

Stepper steppers[maxSteppers]

◆ then

uint32_t then = millis()

◆ writtenToBuffer

volatile uint8_t writtenToBuffer = 0