Add support for Grove tb6612 fng (#4797)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Christian 2023-07-12 04:02:53 +01:00 committed by GitHub
parent cf65bd8ad7
commit e0fd8cd850
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 547 additions and 0 deletions

View file

@ -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

View file

@ -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.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.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.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

View file

@ -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

View file

@ -0,0 +1,208 @@
#pragma once
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/automation.h"
//#include "esphome/core/helpers.h"
/*
Grove_Motor_Driver_TB6612FNG.h
A library for the Grove - Motor Driver(TB6612FNG)
Copyright (c) 2018 seeed technology co., ltd.
Website : www.seeed.cc
Author : Jerry Yip
Create Time: 2018-06
Version : 0.1
Change Log :
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
namespace esphome {
namespace grove_i2c_motor {
enum MotorChannelTypeT {
MOTOR_CHA = 0,
MOTOR_CHB = 1,
};
enum StepperModeTypeT {
FULL_STEP = 0,
WAVE_DRIVE = 1,
HALF_STEP = 2,
MICRO_STEPPING = 3,
};
class GroveMotorDriveTB6612FNG : public Component, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
/*************************************************************
Description
Enter standby mode. Normally you don't need to call this, except that
you have called notStandby() before.
Parameter
Null.
Return
True/False.
*************************************************************/
bool standby();
/*************************************************************
Description
Exit standby mode. Motor driver does't do any action at this mode.
Parameter
Null.
Return
True/False.
*************************************************************/
bool not_standby();
/*************************************************************
Description
Set an new I2C address.
Parameter
addr: 0x01~0x7f
Return
Null.
*************************************************************/
void set_i2c_addr(uint8_t addr);
/*************************************************************
Description
Drive a motor.
Parameter
chl: MOTOR_CHA or MOTOR_CHB
speed: -255~255, if speed > 0, motor moves clockwise.
Note that there is always a starting speed(a starting voltage) for motor.
If the input voltage is 5V, the starting speed should larger than 100 or
smaller than -100.
Return
Null.
*************************************************************/
void dc_motor_run(uint8_t channel, int16_t speed);
/*************************************************************
Description
Brake, stop the motor immediately
Parameter
chl: MOTOR_CHA or MOTOR_CHB
Return
Null.
*************************************************************/
void dc_motor_brake(uint8_t channel);
/*************************************************************
Description
Stop the motor slowly.
Parameter
chl: MOTOR_CHA or MOTOR_CHB
Return
Null.
*************************************************************/
void dc_motor_stop(uint8_t channel);
/*************************************************************
Description
Drive a stepper.
Parameter
mode: 4 driver mode: FULL_STEP,WAVE_DRIVE, HALF_STEP, MICRO_STEPPING,
for more information: https://en.wikipedia.org/wiki/Stepper_motor#/media/File:Drive.png
steps: The number of steps to run, range from -32768 to 32767.
When steps = 0, the stepper stops.
When steps > 0, the stepper runs clockwise. When steps < 0, the stepper runs anticlockwise.
rpm: Revolutions per minute, the speed of a stepper, range from 1 to 300.
Note that high rpm will lead to step lose, so rpm should not be larger than 150.
Return
Null.
*************************************************************/
void stepper_run(StepperModeTypeT mode, int16_t steps, uint16_t rpm);
/*************************************************************
Description
Stop a stepper.
Parameter
Null.
Return
Null.
*************************************************************/
void stepper_stop();
// keeps moving(direction same as the last move, default to clockwise)
/*************************************************************
Description
Keep a stepper running.
Parameter
mode: 4 driver mode: FULL_STEP,WAVE_DRIVE, HALF_STEP, MICRO_STEPPING,
for more information: https://en.wikipedia.org/wiki/Stepper_motor#/media/File:Drive.png
rpm: Revolutions per minute, the speed of a stepper, range from 1 to 300.
Note that high rpm will lead to step lose, so rpm should not be larger than 150.
is_cw: Set the running direction, true for clockwise and false for anti-clockwise.
Return
Null.
*************************************************************/
void stepper_keep_run(StepperModeTypeT mode, uint16_t rpm, bool is_cw);
private:
uint8_t buffer_[16];
};
template<typename... Ts>
class GROVETB6612FNGMotorRunAction : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
public:
TEMPLATABLE_VALUE(uint8_t, channel)
TEMPLATABLE_VALUE(uint16_t, speed)
void play(Ts... x) override {
auto channel = this->channel_.value(x...);
auto speed = this->speed_.value(x...);
this->parent_->dc_motor_run(channel, speed);
}
};
template<typename... Ts>
class GROVETB6612FNGMotorBrakeAction : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
public:
TEMPLATABLE_VALUE(uint8_t, channel)
void play(Ts... x) override { this->parent_->dc_motor_brake(this->channel_.value(x...)); }
};
template<typename... Ts>
class GROVETB6612FNGMotorStopAction : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
public:
TEMPLATABLE_VALUE(uint8_t, channel)
void play(Ts... x) override { this->parent_->dc_motor_stop(this->channel_.value(x...)); }
};
template<typename... Ts>
class GROVETB6612FNGMotorStandbyAction : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
public:
void play(Ts... x) override { this->parent_->standby(); }
};
template<typename... Ts>
class GROVETB6612FNGMotorNoStandbyAction : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
public:
void play(Ts... x) override { this->parent_->not_standby(); }
};
} // namespace grove_i2c_motor
} // namespace esphome

View file

@ -303,6 +303,10 @@ sm2135:
rgb_current: 20mA
cw_current: 60mA
grove_i2c_motor:
id: test_motor
address: 0x14
switch:
- platform: template
name: mpr121_toggle
@ -353,6 +357,17 @@ switch:
Content-Type: application/json
body: Some data
verify_ssl: false
- platform: template
name: open_vent
id: open_vent
optimistic: True
on_turn_on:
then:
- grove_i2c_motor.run:
channel: 1
speed: 255
direction: BACKWARD
id: test_motor
custom_component: