ModbusMaster
ModbusMaster copied to clipboard
Controlling Optidrive E3 with Arduino via modbus RTU
I'm trying to control a frequenty controller (Optidrive E3) with my Arduino Uno. This Optidrive supports the modbus RTU protocol and so does my Arduino Uno (1.8.1 IDE) when using a MAX485 breakout board.
After reading the register map and datasheet of the Optidrive and a lot of info on modbus rtu:
v2.0.1 ModbusMaster version 1.8.1 Arduino IDE version Arduino Uno Max485
I figured out i need to send a function 6 packet to the Optidrive, something like this:
01 06 0001 0009 75A6
01: The Slave Address, i can set this myself on Optidrive.
06: The Function Code 6 (Preset Single Register).
0001: The Data Address of the "control Word" register number.
0001: The value to write: to let motor run
75A6: The CRC-16 (Modbus), i add the other bytes and calculate the CRC.
01 06 0002 0032 4FAB
01: The Slave Address, i can set this myself on Optidrive.
06: The Function Code 6 (Preset Single Register).
0002: The Data Address of the "Frequency Setpoint" register number.
0032: The value to write: If i want to set it too 50hz.
4FAB: The CRC-16 (Modbus), i add the other bytes and calculate the CRC.
So i know what to send but the question is how? (is this even correct?) I found your librarie and it supports function 6 wirte single register. Now i'm trying to figure out how do i send these bytes.
I found your RS485_HalfDuplex and other examples and tried to modify it, but nothing seems to work.
I would greatly apreciate it if you could help me out, i have no clue how to debug it. And don't know where to look for answers. Thanks in advance all the feedback and info is very welcome.
Code:
/*
RS485_HalfDuplex.pde - example using ModbusMaster library to communicate
with EPSolar LS2024B controller using a half-duplex RS485 transceiver.
This example is tested against an EPSolar LS2024B solar charge controller.
See here for protocol specs:
http://www.solar-elektro.cz/data/dokumenty/1733_modbus_protocol.pdf
Library:: ModbusMaster
Author:: Marius Kintel <marius at kintel dot net>
Copyright:: 2009-2016 Doc Walker
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <ModbusMaster.h>
/*!
We're using a MAX485-compatible RS485 Transceiver.
Rx/Tx is hooked up to the hardware serial port at 'Serial'.
The Data Enable and Receiver Enable pins are hooked up as follows:
*/
#define MAX485_DE 3
#define MAX485_RE_NEG 2
// instantiate ModbusMaster object
ModbusMaster node;
void preTransmission()
{
digitalWrite(MAX485_RE_NEG, 1);
digitalWrite(MAX485_DE, 1);
}
void postTransmission()
{
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
}
void setup()
{
pinMode(MAX485_RE_NEG, OUTPUT);
pinMode(MAX485_DE, OUTPUT);
// Init in receive mode <----------- changed to 1 for transmit mode ???
digitalWrite(MAX485_RE_NEG, 1);
digitalWrite(MAX485_DE, 1);
// Modbus communication runs at 57600 baud
Serial.begin(57600);
// Modbus slave ID 1, <---------- transmit to slave ID 1, or become slave ID 1 ?
node.begin(1, Serial);
// Callbacks allow us to configure the RS485 transceiver correctly <---- do i need these ?
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
}
bool state = true;
void loop()
{
//uint8_t result;
// uint16_t data[6];
node.writeSingleRegister(0x0001, 0x0001); //----> enable runMode?
node.writeSingleRegister(0x0002, 0x0010); //----> set Freq?
// // Read 16 registers starting at 0x3100)
// result = node.readInputRegisters(0x3100, 16);
// if (result == node.ku8MBSuccess)
// {
// Serial.print("Vbatt: ");
// Serial.println(node.getResponseBuffer(0x04)/100.0f);
// Serial.print("Vload: ");
// Serial.println(node.getResponseBuffer(0xC0)/100.0f);
// Serial.print("Pload: ");
// Serial.println((node.getResponseBuffer(0x0D) +
// node.getResponseBuffer(0x0E) << 16)/100.0f);
// }
delay(1000);
}
Hi
I am too looking into this, did you make any progress. ?
I have had some success working with these if anyone is still interested. Both controlling and read registers. This is an example of one I am controlling from my phone using thinger.io. Code:
`#define _DEBUG_
#include <SPI.h>
#include <Ethernet.h>
#include <ThingerEthernet.h> //swap .h file for dhcp ThingerEthernetold.h
#include <ModbusMaster.h>
#define MAX485_DE 3
#define MAX485_RE_NEG 2
ModbusMaster node;
ThingerEthernet thing("username", "thingname?", "password?");
int address = 0;
uint16_t writeto = 0x002b;
uint16_t message = 0x0000;
uint16_t readfrom = 0x002c;
uint16_t result1;
uint16_t result;
int pinA0 = A0;
int pinA1 = A1;
int pinA2 = A2;
int wn;
int w = 0; // windspeed
int f = 0; // fan speed
int p = 0; // pressure
int a = 0; // fan amps
int readparam;
void preTransmission()
{
digitalWrite(MAX485_RE_NEG, 1);
digitalWrite(MAX485_DE, 1);
}
void postTransmission()
{
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
}
void setup() {
pinMode(MAX485_RE_NEG, OUTPUT);
pinMode(MAX485_DE, OUTPUT);
// Init in receive mode
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
// Modbus communication runs at 115200 baud
Serial.begin(115200);
// Modbus slave ID 1
node.begin(1, Serial);
// Callbacks allow us to configure the RS485 transceiver correctly
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
bool state = true;
Serial.println("IP");
Serial.println(Ethernet.localIP());
Serial.println("gateway");
Serial.println(Ethernet.gatewayIP());
Serial.println("dns");
Serial.println(Ethernet.dnsServerIP());
Serial.println("subnet");
Serial.println(Ethernet.subnetMask());
}
void loop() {
thing["address"] << [](pson& in){
address=in;
//address = in["address"];
};
switch (address) {
case 1:
node.preTransmission(preTransmission);
result1 = node.writeSingleRegister (0x0000, 0x0001);//register to write to, message
node.postTransmission(postTransmission);
break;
case 2:
node.preTransmission(preTransmission);
result1 = node.writeSingleRegister (0x0000, 0x0006);//register to write to, message
node.postTransmission(postTransmission);
break;
case 3:
node.preTransmission(preTransmission);
result1 = node.writeSingleRegister (0x0000, 0x0004);//register to write to, message
node.postTransmission(postTransmission);
break;
case 4:
node.preTransmission(preTransmission);
result1 = node.writeSingleRegister (0x002b, 0x0001);//register to write to, message
node.postTransmission(postTransmission);
break;
case 5:
node.preTransmission(preTransmission);
result1 = node.writeSingleRegister (0x002b, 0x0007);//register to write to, message
node.postTransmission(postTransmission);
break;
case 6:
node.preTransmission(preTransmission);
result1 = node.writeSingleRegister (0x002b, 0x0008);//register to write to, message
node.postTransmission(postTransmission);
break;
default:
// statements
break;
}
w = analogRead(pinA0);
f = analogRead(pinA1);
p = analogRead(pinA2);
wn = 25.33 * ((w-204.6)*100L/(1023-204.6));
thing["Data"] >> [](pson& out){
out["Wind Speed"] = wn;
out["Fan Speed"] = f;
out["Pressure"] = p;
out["Parameter"] = readparam;
};
Serial.println(address);
//Serial.println(writeto);
//Serial.println(message);
Serial.println(wn);
//uint16_t data[6];
// Toggle the coil at address 0x0002 (Manual Load Control)
//result = node.writeSingleCoil(0x0002, state);
//state = !state;
//0x002b,0x000e
//delay(100);
// Read 16 registers starting at 0x3100)
result = node.readInputRegisters(readfrom, 1);
if (result == node.ku8MBSuccess)
{
Serial.print("Parameter value: ");
//Serial.println(node.getResponseBuffer(0x00));
readparam = node.getResponseBuffer(0x00);
Serial.print(readparam);
}
delay(1000);
thing.handle();
}`
For anyone still interested, I have been working on this lately for a project I'm working on. I also ran into some frustration trying to get things to work properly. Two things that have made a difference for me is first, using a uart serial interface with my MAX485 module (do not use software serial); and second, ModbusMaster is a zero-based addressing library but Optidrive E3 is 1-based addressing, so make sure you subtract 1 from the documented register.
Hi
I am trying to read the holding registers of an Invertek OptiDrive drive vfd and with your example i am still getting 226 or E2 in hex. i also raisedthe baudrate to 115200 and also set the timeout to 5 secs to no avail. I suspect i need to toggle a parameter for modbus monitoring though and am scanning the manual in order to get it to work.
Anyone had similar issues ?
Jason