Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Grove tb6612 fng #4797

Merged
merged 45 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
e67a0e5
add motor implementation
max246 May 8, 2023
4d8f309
fix issues
max246 May 9, 2023
bbef99b
fixes
max246 May 9, 2023
76eafdb
add owner
max246 May 9, 2023
06c51b2
add test
max246 May 9, 2023
cac1a98
update i2c
max246 May 9, 2023
159d6a8
rename
max246 May 9, 2023
4ef4257
fix lint
max246 May 9, 2023
2dfd136
fix lint
max246 May 9, 2023
85927b8
fix lint
max246 May 9, 2023
c8c4fc5
fix lint
max246 May 9, 2023
98b341f
remove lines
max246 May 9, 2023
715f589
add one line for lint
max246 May 9, 2023
954f991
add one line for lint
max246 May 9, 2023
0f93347
update fail message
max246 May 11, 2023
1018d3a
Merge branch 'dev' into FEATURE/grove-TB6612FNG
max246 May 12, 2023
52dce9e
remove name
max246 May 12, 2023
f862296
remove delay
max246 May 12, 2023
1c5b7e6
update import
max246 May 14, 2023
6b185be
fixes
max246 May 14, 2023
a74f305
only compile for arduino
max246 May 14, 2023
7ecc7f9
update limitation
max246 May 14, 2023
a737b2e
fixes
max246 May 14, 2023
e09ae06
move import lib
max246 May 14, 2023
426bf9f
Revert "update import"
max246 May 14, 2023
a5fb0a4
clean up test
max246 May 14, 2023
15fefb7
Merge branch 'dev' into FEATURE/grove-TB6612FNG
max246 May 14, 2023
678a26c
Update esphome/components/grove_i2c_motor/grove_i2c_motor.cpp
max246 May 14, 2023
0addf7a
Update esphome/components/grove_i2c_motor/grove_i2c_motor.cpp
max246 May 14, 2023
d45893a
Update esphome/components/grove_i2c_motor/grove_i2c_motor.cpp
max246 May 14, 2023
ad57281
tidy up code
max246 May 14, 2023
70826b4
fix parentd
max246 May 14, 2023
f396439
fix parent
max246 May 14, 2023
f42727e
Merge branch 'dev' into FEATURE/grove-TB6612FNG
max246 May 15, 2023
2fd6abd
Merge branch 'dev' into FEATURE/grove-TB6612FNG
max246 May 17, 2023
341cbc1
Merge branch 'dev' into FEATURE/grove-TB6612FNG
max246 Jul 8, 2023
6900fa5
add tests
max246 Jul 11, 2023
ee7bcf3
Merge branch 'dev' into FEATURE/grove-TB6612FNG
max246 Jul 11, 2023
386322f
fix test
max246 Jul 11, 2023
e6a0036
fix test
max246 Jul 11, 2023
b96d6e5
fix test
max246 Jul 11, 2023
9b73d0d
fix test
max246 Jul 11, 2023
8711736
fix test
max246 Jul 11, 2023
dcfb206
Update actions
jesserockz Jul 12, 2023
fe5be90
Update actions in test
jesserockz Jul 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ esphome/components/gp8403/* @jesserockz
esphome/components/gpio/* @esphome/core
esphome/components/gps/* @coogle
esphome/components/graph/* @synco
esphome/components/grove_i2c_motor/* @max246
esphome/components/growatt_solar/* @leeuwte
esphome/components/haier/* @paveldn
esphome/components/havells_solar/* @sourabhjaiswal
Expand Down
152 changes: 152 additions & 0 deletions esphome/components/grove_i2c_motor/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import i2c

from esphome.const import (
CONF_ID,
CONF_CHANNEL,
CONF_SPEED,
CONF_DIRECTION,
)

DEPENDENCIES = ["i2c"]

CODEOWNERS = ["@max246"]

grove_i2c_motor_ns = cg.esphome_ns.namespace("grove_i2c_motor")
GROVE_TB6612FNG = grove_i2c_motor_ns.class_(
"GroveMotorDriveTB6612FNG", cg.Component, i2c.I2CDevice
)
GROVETB6612FNGMotorRunAction = grove_i2c_motor_ns.class_(
"GROVETB6612FNGMotorRunAction", automation.Action
)
GROVETB6612FNGMotorBrakeAction = grove_i2c_motor_ns.class_(
"GROVETB6612FNGMotorBrakeAction", automation.Action
)
GROVETB6612FNGMotorStopAction = grove_i2c_motor_ns.class_(
"GROVETB6612FNGMotorStopAction", automation.Action
)
GROVETB6612FNGMotorStandbyAction = grove_i2c_motor_ns.class_(
"GROVETB6612FNGMotorStandbyAction", automation.Action
)
GROVETB6612FNGMotorNoStandbyAction = grove_i2c_motor_ns.class_(
"GROVETB6612FNGMotorNoStandbyAction", automation.Action
)

DIRECTION_TYPE = {
"FORWARD": 1,
"BACKWARD": 2,
}

CONFIG_SCHEMA = (
cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(GROVE_TB6612FNG),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x14))
)


async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)


@automation.register_action(
"grove_i2c_motor.motor_run",
GROVETB6612FNGMotorRunAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
cv.Required(CONF_CHANNEL): cv.templatable(cv.int_range(min=0, max=1)),
cv.Required(CONF_SPEED): cv.templatable(cv.int_range(min=0, max=255)),
cv.Required(CONF_DIRECTION): cv.enum(DIRECTION_TYPE, upper=True),
}
),
)
async def grove_i2c_motor_run_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])

template_channel = await cg.templatable(config[CONF_CHANNEL], args, int)
template_speed = await cg.templatable(config[CONF_SPEED], args, cg.uint16)
template_speed = (
template_speed if config[CONF_DIRECTION] == "FORWARD" else -template_speed
)
cg.add(var.set_channel(template_channel))
cg.add(var.set_speed(template_speed))
return var


@automation.register_action(
"grove_i2c_motor.motor_break",
GROVETB6612FNGMotorBrakeAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
cv.Required(CONF_CHANNEL): cv.templatable(cv.int_range(min=0, max=1)),
}
),
)
async def grove_i2c_motor_break_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])

template_channel = await cg.templatable(config[CONF_CHANNEL], args, int)
cg.add(var.set_channel(template_channel))
return var


@automation.register_action(
"grove_i2c_motor.motor_stop",
GROVETB6612FNGMotorStopAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
cv.Required(CONF_CHANNEL): cv.templatable(cv.int_range(min=0, max=1)),
}
),
)
async def grove_i2c_motor_stop_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])

template_channel = await cg.templatable(config[CONF_CHANNEL], args, int)
cg.add(var.set_channel(template_channel))
return var


@automation.register_action(
"grove_i2c_motor.standby",
GROVETB6612FNGMotorStandbyAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
}
),
)
async def grove_i2c_motor_standby_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])

return var


@automation.register_action(
"grove_i2c_motor.no_standby",
GROVETB6612FNGMotorNoStandbyAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
}
),
)
async def grove_i2c_motor_no_standby_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])

return var
171 changes: 171 additions & 0 deletions esphome/components/grove_i2c_motor/grove_i2c_motor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#include "grove_i2c_motor.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"

namespace esphome {
namespace grove_i2c_motor {

static const char *const TAG = "GroveMotorDriveTB6612FNG";

static const uint8_t GROVE_MOTOR_DRIVER_I2C_CMD_BRAKE = 0x00;
static const uint8_t GROVE_MOTOR_DRIVER_I2C_CMD_STOP = 0x01;
static const uint8_t GROVE_MOTOR_DRIVER_I2C_CMD_CW = 0x02;
static const uint8_t GROVE_MOTOR_DRIVER_I2C_CMD_CCW = 0x03;
static const uint8_t GROVE_MOTOR_DRIVER_I2C_CMD_STANDBY = 0x04;
static const uint8_t GROVE_MOTOR_DRIVER_I2C_CMD_NOT_STANDBY = 0x05;
static const uint8_t GROVE_MOTOR_DRIVER_I2C_CMD_STEPPER_RUN = 0x06;
static const uint8_t GROVE_MOTOR_DRIVER_I2C_CMD_STEPPER_STOP = 0x07;
static const uint8_t GROVE_MOTOR_DRIVER_I2C_CMD_STEPPER_KEEP_RUN = 0x08;
static const uint8_t GROVE_MOTOR_DRIVER_I2C_CMD_SET_ADDR = 0x11;

void GroveMotorDriveTB6612FNG::dump_config() {
ESP_LOGCONFIG(TAG, "GroveMotorDriveTB6612FNG:");
LOG_I2C_DEVICE(this);
}

void GroveMotorDriveTB6612FNG::setup() {
ESP_LOGCONFIG(TAG, "Setting up Grove Motor Drive TB6612FNG ...");
if (!this->standby()) {
this->mark_failed();
return;
}
}

bool GroveMotorDriveTB6612FNG::standby() {
uint8_t status = 0;
if (this->write_register(GROVE_MOTOR_DRIVER_I2C_CMD_STANDBY, &status, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Set standby failed!");
this->status_set_warning();
return false;
}
return true;
}

bool GroveMotorDriveTB6612FNG::not_standby() {
uint8_t status = 0;
if (this->write_register(GROVE_MOTOR_DRIVER_I2C_CMD_NOT_STANDBY, &status, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Set not standby failed!");
this->status_set_warning();
return false;
}
return true;
}

void GroveMotorDriveTB6612FNG::set_i2c_addr(uint8_t addr) {
if (addr == 0x00 || addr >= 0x80) {
return;
}
if (this->write_register(GROVE_MOTOR_DRIVER_I2C_CMD_SET_ADDR, &addr, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Set new i2c address failed!");
this->status_set_warning();
return;
}
this->set_i2c_address(addr);
}

void GroveMotorDriveTB6612FNG::dc_motor_run(uint8_t channel, int16_t speed) {
speed = clamp<int16_t>(speed, -255, 255);

buffer_[0] = channel;
if (speed >= 0) {
buffer_[1] = speed;
} else {
buffer_[1] = (uint8_t) (-speed);
}

if (speed >= 0) {
if (this->write_register(GROVE_MOTOR_DRIVER_I2C_CMD_CW, buffer_, 2) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Run motor failed!");
this->status_set_warning();
return;
}
} else {
if (this->write_register(GROVE_MOTOR_DRIVER_I2C_CMD_CCW, buffer_, 2) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Run motor failed!");
this->status_set_warning();
return;
}
}
}

void GroveMotorDriveTB6612FNG::dc_motor_brake(uint8_t channel) {
if (this->write_register(GROVE_MOTOR_DRIVER_I2C_CMD_BRAKE, &channel, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Break motor failed!");
this->status_set_warning();
return;
}
}

void GroveMotorDriveTB6612FNG::dc_motor_stop(uint8_t channel) {
if (this->write_register(GROVE_MOTOR_DRIVER_I2C_CMD_STOP, &channel, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Stop dc motor failed!");
this->status_set_warning();
return;
}
}

void GroveMotorDriveTB6612FNG::stepper_run(StepperModeTypeT mode, int16_t steps, uint16_t rpm) {
uint8_t cw = 0;
// 0.1ms_per_step
uint16_t ms_per_step = 0;

if (steps > 0) {
cw = 1;
}
// stop
else if (steps == 0) {
this->stepper_stop();
return;
} else if (steps == INT16_MIN) {
steps = INT16_MAX;
} else {
steps = -steps;
}

rpm = clamp<uint16_t>(rpm, 1, 300);

ms_per_step = (uint16_t) (3000.0 / (float) rpm);
buffer_[0] = mode;
buffer_[1] = cw; //(cw=1) => cw; (cw=0) => ccw
buffer_[2] = steps;
buffer_[3] = (steps >> 8);
buffer_[4] = ms_per_step;
buffer_[5] = (ms_per_step >> 8);

if (this->write_register(GROVE_MOTOR_DRIVER_I2C_CMD_STEPPER_RUN, buffer_, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Run stepper failed!");
this->status_set_warning();
return;
}
}

void GroveMotorDriveTB6612FNG::stepper_stop() {
if (this->write_register(GROVE_MOTOR_DRIVER_I2C_CMD_STEPPER_STOP, nullptr, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Send stop stepper failed!");
this->status_set_warning();
return;
}
}

void GroveMotorDriveTB6612FNG::stepper_keep_run(StepperModeTypeT mode, uint16_t rpm, bool is_cw) {
// 4=>infinite ccw 5=>infinite cw
uint8_t cw = (is_cw) ? 5 : 4;
// 0.1ms_per_step
uint16_t ms_per_step = 0;

rpm = clamp<uint16_t>(rpm, 1, 300);
ms_per_step = (uint16_t) (3000.0 / (float) rpm);

buffer_[0] = mode;
buffer_[1] = cw; //(cw=1) => cw; (cw=0) => ccw
buffer_[2] = ms_per_step;
buffer_[3] = (ms_per_step >> 8);

if (this->write_register(GROVE_MOTOR_DRIVER_I2C_CMD_STEPPER_KEEP_RUN, buffer_, 4) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Write stepper keep run failed");
this->status_set_warning();
return;
}
}
} // namespace grove_i2c_motor
} // namespace esphome