/* xdrv_34_wemos_motor_v1.ino - Support for I2C WEMOS motor shield (6612FNG) Copyright (C) 2021 Denis Sborets, Theo Arends, Peter Franck 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, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef USE_I2C #ifdef USE_WEMOS_MOTOR_V1 /*********************************************************************************************\ * WEMOS_MOTOR_V1 - DC motor driver shield (6612FNG) v 1.0.0 * WEMOS_MOTOR_V2 - DC motor driver shield (6612FNG) v 2.0.0 (#define WEMOS_MOTOR_V2) * * I2C Address: 0x30 * * Command format: * driver44 ,,{,} * command: * RESET - reset a motor shield * SETMOTOR - set motor state * motor: * 0 - motor A * 1 - motor B * 2 - both motors (V2 only) * direction: * 0 - short break * 1 - CCW * 2 - CW * 3 - stop * 4 - standby * duty (optional): * 0 - 100% (100% by default) \*********************************************************************************************/ #define XDRV_34 34 #define XI2C_44 44 // See I2CDEVICES.md #ifndef WEMOS_MOTOR_V1_ADDR #define WEMOS_MOTOR_V1_ADDR 0x30 // Default I2C address 0x30 #endif #ifndef WEMOS_MOTOR_V1_FREQ #define WEMOS_MOTOR_V1_FREQ 1000 // Default frequency #endif #define MOTOR_A 0 #define MOTOR_B 1 #define SHORT_BRAKE 0 #define CCW 1 #define CW 2 #define STOP 3 #define STANDBY 4 #ifdef WEMOS_MOTOR_V2 // Support latest Lolin board // #define DEBUG_WEMOS_MOTOR // be more verbose #define WEMOS_MOTOR_PID_V2 0x02 // Product ID of V2.0.0 #define WMTR_V2_BUFFSZ 5 // I2C command buffer size enum WEMOS_MOTOR_V2_CMD { WV2_GET_SLAVE_STATUS = 0x01, WV2_RESET_SLAVE, WV2_CHANGE_I2C_ADDRESS, WV2_CHANGE_STATUS, WV2_CHANGE_FREQ, WV2_CHANGE_DUTY }; enum WEMOS_MOTOR_V2_STATUS { WV2_MOTOR_STATUS_STOP = 0x00, WV2_MOTOR_STATUS_CCW, WV2_MOTOR_STATUS_CW, WV2_MOTOR_STATUS_SHORT_BRAKE, WV2_MOTOR_STATUS_STANDBY }; enum WEMOS_MOTOR_V2_CHANNEL { WV2_MOTOR_CH_A=0x00, WV2_MOTOR_CH_B, WV2_MOTOR_CH_BOTH }; const char WemosMotorDriver[] = "WEMOS_MOTOR_V2"; #else // WEMOS_MOTOR_V2 const char WemosMotorDriver[] = "WEMOS_MOTOR_V1"; #endif // WEMOS_MOTOR_V2 struct WMOTORV1 { bool detected = false; uint8_t motor; } WMotorV1; void WMotorV1Detect(void) { if (I2cSetDevice(WEMOS_MOTOR_V1_ADDR)) { #ifdef WEMOS_MOTOR_V2 uint8_t i2c_data[WMTR_V2_BUFFSZ]; // Check product ID & version i2c_data[0] = WV2_GET_SLAVE_STATUS; WMotorV2command(i2c_data, 1); if (i2c_data[0] == WEMOS_MOTOR_PID_V2) { #ifdef DEBUG_WEMOS_MOTOR AddLog(LOG_LEVEL_INFO, PSTR("WEM: %s Rev. %u found"), WemosMotorDriver, i2c_data[1]); #endif // DEBUG_WEMOS_MOTOR } else { return; } #endif // WEMOS_MOTOR_V2 WMotorV1.detected = true; I2cSetActiveFound(WEMOS_MOTOR_V1_ADDR, WemosMotorDriver); WMotorV1Reset(); } } void WMotorV1Reset(void) { WMotorV1SetFrequency(WEMOS_MOTOR_V1_FREQ); } void WMotorV1SetFrequency(uint32_t freq) { #ifdef WEMOS_MOTOR_V2 uint8_t i2c_data[WMTR_V2_BUFFSZ]; i2c_data[0] = WV2_CHANGE_FREQ; i2c_data[1] = WV2_MOTOR_CH_BOTH; i2c_data[2] = (uint8_t)(freq & 0xff); i2c_data[3] = (uint8_t)((freq >> 8) & 0xff); i2c_data[4] = (uint8_t)((freq >> 16) & 0xff); WMotorV2command(i2c_data, 5); #else // WEMOS_MOTOR_V2 Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR); Wire.write(((byte)(freq >> 16)) & (byte)0x0f); Wire.write((byte)(freq >> 16)); Wire.write((byte)(freq >> 8)); Wire.write((byte)freq); Wire.endTransmission(); #endif // WEMOS_MOTOR_V2 } void WMotorV1SetMotor(uint8_t motor, uint8_t dir, float pwm_val) { #ifdef WEMOS_MOTOR_V2 uint8_t i2c_data[WMTR_V2_BUFFSZ]; // send command uint8_t cmd = dir; if (cmd == 0 || cmd == 3) { cmd ^= 3; // short brake and stop swapped } i2c_data[0] = WV2_CHANGE_STATUS; i2c_data[1] = (uint8_t)motor; i2c_data[2] = (uint8_t)cmd; WMotorV2command(i2c_data, 3); #ifdef DEBUG_WEMOS_MOTOR AddLog(LOG_LEVEL_INFO, PSTR("WEM: Ch: %u Cmd: %u"), motor, cmd); #endif // DEBUG_WEMOS_MOTOR // set duty cycle uint16_t duty = (pwm_val > 100.0) ? 10000 : (uint16_t)(pwm_val * 100); #ifdef DEBUG_WEMOS_MOTOR AddLog(LOG_LEVEL_INFO, PSTR("WEM: Duty: %u"), duty); #endif // DEBUG_WEMOS_MOTOR i2c_data[0] = WV2_CHANGE_DUTY; i2c_data[1] = (uint8_t)motor; i2c_data[2] = (uint8_t)(duty & 0xff); i2c_data[3] = (uint8_t)((duty >> 8) & 0xff); WMotorV2command(i2c_data,4); #else // WEMOS_MOTOR_V2 Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR); Wire.write(motor | (byte)0x10); Wire.write(dir); uint16_t _pwm_val = uint16_t(pwm_val * 100); if (_pwm_val > 10000) { _pwm_val = 10000; } Wire.write((byte)(_pwm_val >> 8)); Wire.write((byte)_pwm_val); Wire.endTransmission(); #endif // WEMOS_MOTOR_V2 } bool WMotorV1Command(void) { uint8_t args_count = 0; if (XdrvMailbox.data_len > 0) { args_count = 1; } else { return false; } for (uint32_t idx = 0; idx < XdrvMailbox.data_len; idx++) { if (' ' == XdrvMailbox.data[idx]) { XdrvMailbox.data[idx] = ','; } if (',' == XdrvMailbox.data[idx]) { args_count++; } } UpperCase(XdrvMailbox.data, XdrvMailbox.data); char *command = strtok(XdrvMailbox.data, ","); if (strcmp(command, "RESET") == 0) { #ifdef WEMOS_MOTOR_V2 // do a 'real' reset uint8_t i2c_data[WMTR_V2_BUFFSZ]; i2c_data[0] = WV2_RESET_SLAVE; WMotorV2command(i2c_data, 1); #endif // WEMOS_MOTOR_V2 WMotorV1Reset(); Response_P(PSTR("{\"%s\":{\"RESET\":\"OK\"}}"), WemosMotorDriver); return true; } if (strcmp(command, "SETMOTOR") == 0) { if (args_count >= 3) { int motor = atoi(strtok(NULL, ",")); int dir = atoi(strtok(NULL, ",")); int duty = 100; if (args_count == 4) { duty = atoi(strtok(NULL, ",")); } WMotorV1SetMotor(motor, dir, duty); Response_P(PSTR("{\"%s\":{\"SETMOTOR\":\"OK\"}}"), WemosMotorDriver); return true; } } return false; } #ifdef WEMOS_MOTOR_V2 void WMotorV2command(uint8_t *data, uint8_t len) // process V2 request { int i; Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR); for (i = 0; i < len; i++) { Wire.write(data[i]); } Wire.endTransmission(); if (data[0] == WV2_GET_SLAVE_STATUS) { Wire.requestFrom(WEMOS_MOTOR_V1_ADDR, 2); } else { Wire.requestFrom(WEMOS_MOTOR_V1_ADDR, 1); } i = 0; bzero(data, WMTR_V2_BUFFSZ); while (Wire.available()) { data[i] = Wire.read(); i++; } } #endif // WEMOS_MOTOR_V2 /*********************************************************************************************\ * Interface \*********************************************************************************************/ bool Xdrv34(uint8_t function) { if (!I2cEnabled(XI2C_44)) { return false; } bool result = false; if (FUNC_INIT == function) { WMotorV1Detect(); } else if (WMotorV1.detected) { switch (function) { case FUNC_COMMAND_DRIVER: if (XI2C_44 == XdrvMailbox.index) { result = WMotorV1Command(); } break; } } return result; } #endif // USE_WEMOS_MOTOR_V1 #endif // USE_IC2