Hi.
I have an issue with converting 1 value to 2 parameters.
I have a predetermined set of parameters and way of handling information.
The unit I am trying to make will replicate an already done production unit.
Different is that the real unit has a price tag of 500.000NOK, the control card alone is 30.000NOK.
I am trying to replicate the communication and behavior of this unit with an Arduino MKR WIFI1010, MKR Can Shield, MKR Modbus Shield, MKR ENV Shield, homemade MKR Stepper motor and RTC Shield. A total price tag of around 1.000NOK.
So my problem now is the RTC, or storing the RTC Unixtime information into 2 seperate parameters.
The information is set through our inhouse test SW called ETU.
When i send the information from Master (PC with ETU) over modbus to Slave/ModbusRTUServer, the Unixtime 1625058395 is seperated into 2 parameters, I know this due to I am sniffing the modbus line and get this: slave id: f0 PDU: 10 07 d7 00 02 04 60 dc 6c 5b CRC: 2d db
f0 =address 240
10 = Modbus command, FC16, Preset Multiple Register
07 d7 = Start adress, 2007 + 40001(due to its a holding register) = 42008
00 02 = how many addresses, 2
04 = number of bits
60 dc = first set of bits for the first address, 42008 = 60 dc (24 796)
6c 5b = second set of bits for second address, 42009 = 6c 5b (27 739)
So instead of adding them together to get 52 535, i need the full set 60 dc 6c 5b = 1625058395.
And i get this ok with this code, and can from this set the RTC DS3231 with the incoming Unixtime.
long TimeNow;
int Hi;
int Lo;
// See if new data has arrived in 2007 and 2008 holding
void timeCheck() {
if (ModbusRTUServer.holdingRegisterRead(2007) > 0) {
Serial.println(ModbusRTUServer.holdingRegisterRead(2007));
Serial.println(ModbusRTUServer.holdingRegisterRead(2008));
TimeNow = (ModbusRTUServer.holdingRegisterRead(2007) << 16) | ModbusRTUServer.holdingRegisterRead(2008);
Serial.print(TimeNow);
Serial.println("TimeCheck");
setupTime(TimeNow);
Serial.println("Time is set, Sir");
}
}
void setupTime(int unix) {
rtc.adjust(DateTime(unix));
Serial.println("SetupTime");
checkTime = false;
EEPROM_WRITE(EEPROM_TimeSet, 1);
}
Now that the RTC time is set, i need to update the 2 parameters with the information from the RTC.
And i have tried but end up with the wrong information due to my lack in information and knowhow.
My latest attempt was this, but it gives me this (ignore that the unix time is not the same as above, its from 2 different attempts):
15:22:53.555 -> 1625030656Hi UpdateTime
15:22:53.555 -> 28717Lo UpdateTime
void updateTime() {
DateTime now = rtc.now();
TimeNow = now.unixtime();
Hi = TimeNow & 0xFFFF0000;
Lo = TimeNow & 0x0000FFFF;
Serial.print(Hi);
Serial.println("Hi UpdateTime");
Serial.print(Lo);
Serial.println("Lo UpdateTime");
ModbusRTUServer.holdingRegisterWrite(2007, Hi);
ModbusRTUServer.holdingRegisterWrite(2008, Lo);
ModbusRTUServer.inputRegisterWrite(26, TimeNow);
}
I have tried a couple of different methods, but to no success.
I will post the full code, but it is not pretty, but functional. It works. ![]()
So if someone could explain to me what I am doing wrong, and hopefully how to correct it, or point me in the direction of the correct syntax I would be ever so grateful.
// RS485 ModBus Shield
/* Modbus Commands7
ModbusServer::configureHoldingRegisters(int startAddress, int nb)
ModbusServer::configureInputRegisters(int startAddress, int nb)
ModbusServer::holdingRegisterRead(int address)
ModbusServer::inputRegisterRead(int address)
ModbusServer::holdingRegisterWrite(int address, uint16_t value)
ModbusServer::inputRegisterWrite(int address, uint16_t value)
*/
#include <ArduinoRS485.h> // ArduinoModbus depends on the ArduinoRS485 library
#include <ArduinoModbus.h> // Communication
// ENV Shield
#include <Arduino_MKRENV.h> // Sensors from the MKR ENV SHIELD
//RTC
#include "RTClib.h"
RTC_DS3231 rtc;
// FAKE EEPROM. Using SD card as EEPROM.
#include <SPI.h>
#include <SD.h>
File myFile;
// Stepper Motor
#include <Stepper.h>
// ***************************************************************************
// ********** VARIABLES ******************************************************
// ***************************************************************************
// MODBUS (IF NEEDED)
// DO (Settings)
// Time
long TimeNow;
int Hi;
int Lo;
bool checkTime = true;
// Last MILLIS
float lastEEPROMUpdate;
float lastInternalUpdate;
float lastENV;
// Motor AI (LIVE DATA)
int motorRPM = 0;
int motorTemp1 = 0;
int motorTemp2 = 0;
// Battery AI (LIVE DATA)
int battV = 48;
int battSOC = 100;
int battSOH = 100;
int battCellV[12] = {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4} ;
int battCellCap[12] = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3};
// ENV (LIVE data)
float temperature = 0; // On ENV Shield
float pressure = 0; // On ENV Shield
//RTC (LIVE DATA)
// FAKE EEPROM Using SD card. File Names
String EEPROM_ModbusID = "ModID.txt";
String EEPROM_MaxSpeed = "MaxSpeed.txt";
String EEPROM_MaxTurns = "MaxTurns.txt";
String EEPROM_TimeSet = "TimeSet.txt";
// Stepper
const int stepsPerRevolution = 2048;
int motorMaxSpeed = 7.5; // ModBus = 41202
int motorMaxStemTurns = 10; // ModBus = 41205
int motorGearRatio = 89; // ModBus = 42201
// PHYSICAL SETTINGS
int motorPhysicalGear = 89;
int motorPhysicalMaxSpeed = 900;
float currentPos = 0;
float currentSpeed = 0;
float commandedPos = 0;
// Init Stepper
Stepper myStepper(stepsPerRevolution, 5, 2, 1, 0); // Init stepper
// ***************************************************************************
// ********** SETUP **********************************************************
// ***************************************************************************
void setup() {
// SERIAL FOR DEBUGGING (The text takes ALOT of space, remove or reformat if needed)
Serial.begin(9600);
delay(5000);
Serial.println("Serial RS232 (DEBUGGING PORT) running -----------------------------");
// Init SD card, to be able to read EEPROM
if (!SD.begin(4)) {
Serial.println("SD Init failed!");
while (1);
}
Serial.println("SD Init done.");
initEEPROM();
// Start the Modbus RTU server, with (slave) id EEPROM_READ(EEPROM_ModbusID)
if (!ModbusRTUServer.begin(EEPROM_READ(EEPROM_ModbusID), 9600)) {
Serial.println("Failed to start Modbus RTU Server!");
while (1);
}
else {
Serial.println("ModBus Running!");
Serial.print("ModBus Adress: ");
Serial.println(EEPROM_READ(EEPROM_ModbusID));
}
// Start ENV Shield
if (!ENV.begin()) {
Serial.println("Failed to initialize MKR ENV shield!");
while (1);
}
else {
Serial.println("MKR ENV shield Started!");
}
// MODBUS Index Setup
//ModbusRTUServer.configureHoldingRegisters(0x00, 4); //40001-40004 //works alone, but not with multiple, does not work, yet
//ModbusRTUServer.configureHoldingRegisters(0x12c, 15); //40301-40315 // works alone, but not with multiple, does not work yet
ModbusRTUServer.configureHoldingRegisters(0x00, 2616); //40001-42616 // too large, and gives alot of parameters that should not exist, and cant make boot with that.
ModbusRTUServer.configureInputRegisters(0, 2534); //30001-32534
// MODBUS SETUP ACCORDING TO EEPROM
ModbusRTUServer.holdingRegisterWrite(1201, EEPROM_READ(EEPROM_MaxSpeed) * 10); // *10 because modbus
ModbusRTUServer.holdingRegisterWrite(1204, EEPROM_READ(EEPROM_MaxTurns) * 10); // *10 because modbus
// Set time if flag is set
if (EEPROM_READ(EEPROM_TimeSet)) {
updateTime();
checkTime = false;
}
// MODBUS Generate static info
generateModbus();
Serial.println("G2i FAKiE has started!");
}
// ***************************************************************************
// ********** LOOP ***********************************************************
// ***************************************************************************
void loop() {
// Get the latest time from RTC
// poll for Modbus RTU requests
ModbusRTUServer.poll();
// Update temp and preassure
ENVupdate();
// Change late?? Updates the internal variables according modbus registers
updateInternalFromModbus();
// Update EEPROM with new information if changed
EEPROM_Check();
operateModbus();
if (checkTime) {
timeCheck();
}
}
// ***************************************************************************
// ********** FUNCTIONS ******************************************************
// ***************************************************************************
// Updates the enviromental parameters internally
void ENVupdate() {
if (lastENV + 1000 <= millis()) {
temperature = ENV.readTemperature();
pressure = ENV.readPressure();
//add RTC here
if(!checkTime){updateTime();}
DateTime now = rtc.now();
Serial.print(now.unixtime());
Serial.println("Loop Time");
lastENV = millis();
}
}
// READ EEPROM from SD card
int EEPROM_READ(String file) {
myFile = SD.open(file); // Open access to file. Omly 1 file can be accessed at a time
int result; // the value to return on this command
if (myFile) {
char temp_string[6]; // store information in array due to ASCII read from the file.
unsigned int index = 0; // counter
char next_char;
// read from the file until there's nothing else in it
while (myFile.available()) {
next_char = myFile.read();
temp_string[index++] = next_char;
}
result = atoi(temp_string); // convert the array containing ASCII to int.
// close the file
myFile.close();
} else {
// if the file didn't open, print an error
Serial.println(file);
Serial.println("READ. Error opening file");
}
return (result);
}
// WRITE EEPROM to SD card. Remove file to remove old data, create new file with new info.
// File name | Value to store
void EEPROM_WRITE(String file, int i) {
SD.remove(file); // delete old data
myFile = SD.open(file, FILE_WRITE); // open write access to file, if file does noe excist, make new.
// As long as the file is open and available, write in the information in i to the file.
if (myFile) {
myFile.println(i);
// close the file
myFile.close();
Serial.print(file);
Serial.print(" = ");
Serial.println(i);
Serial.println("WRITE. DONE");
} else {
// if the file didn't open, print an error:
Serial.println("WRITE. Error opening file");
}
}
// ------------------------------------------------------------------------------------------------------
// MODBUS
// ------------------------------------------------------------------------------------------------------
// Do operations according to modbus feedback
void operateModbus(){
if(ModbusRTUServer.holdingRegisterRead(2304) == 12){
CleanSlateProtocol();
ModbusRTUServer.holdingRegisterWrite(2304, 0);
}
if(ModbusRTUServer.holdingRegisterRead(2000) == 3){
//resetFunc(); //call reset
ModbusRTUServer.holdingRegisterWrite(2000, 0);
}
}
// Generate ModBus Static information Information- REMEMBER -1
void generateModbus() {
Serial.println("Generating Static ModBus Parameters");
// COC APPL SW INFO
int swAPPL[] = {1, 98, 20, 13, 25, 6, 2021, 6};
int swBOOT[] = {1, 99, 20, 16, 25, 6, 2021, 6};
setIRegisters(2004, 7, swAPPL); // COC SW APPL
setIRegisters(2012, 7, swBOOT); // COC SW BOOT
// MODBUS Message info, error counter etc. NO NEED TO GENERATEGENERATE
// MCE APPL SW INFO
ModbusRTUServer.inputRegisterWrite(2050, 1); // MCE HW VERSION
ModbusRTUServer.inputRegisterWrite(2051, 1); // Q
setIRegisters(2052, 7, swAPPL); // MCE SW APPL
// BMS APPL SW INFO
ModbusRTUServer.inputRegisterWrite(2060, 1); // BMS HW VERSION
ModbusRTUServer.inputRegisterWrite(2061, 1); // Q
setIRegisters(2062, 7, swAPPL); // BMS SW APPL
// MCE BOOT SW INFO
setIRegisters(2079, 7, swBOOT); // MCE SW BOOT
// BMS BOOT SW INFO
setIRegisters(2089, 7, swBOOT); // BMS SW BOOT
ModbusRTUServer.holdingRegisterWrite(1203, 10); // Stop Distance from end
ModbusRTUServer.holdingRegisterWrite(1205, 20); // Fast Sig
ModbusRTUServer.holdingRegisterWrite(1206, 500); // Slow Sig
}
// Write modbus Input Registers according to sendt data
void setIRegisters(int start, int nb, int a[]) {
for (int i = 0; i <= nb; i++) {
ModbusRTUServer.inputRegisterWrite(start + i, a[i]);
}
}
// Write modbus Holding Regisers according to sendt data
void setHRegisters(int start, int nb, int a[]) {
for (int i = 0; i <= nb; i++) {
ModbusRTUServer.holdingRegisterWrite(start + i, a[i]);
}
}
// UPDATE internal var from polled Modbus, and update canbus registers
void updateInternalFromModbus() {
if (lastInternalUpdate + 1000 <= millis()) {
lastInternalUpdate = millis();
// read ModBus holding registers
// Verify max speed acoording to physical
motorMaxSpeed = ModbusRTUServer.holdingRegisterRead(1201) / 10; //41202
motorMaxStemTurns = ModbusRTUServer.holdingRegisterRead(1204) / 10; //41205
commandedPos = ModbusRTUServer.holdingRegisterRead(0) / 100; //400001
// Run motor command
if (ModbusRTUServer.holdingRegisterRead(1) == 6) {
stepperMove();
//set motor status moving, LATER
//set MCS Status active, LATER
//set Torque, LATER
//
//When done
//Set last max torque.
//reset modbus reg to 0
ModbusRTUServer.holdingRegisterWrite(1, 0);
Serial.println("Motor Move DONE.");
}
}
}
// Check and update EEPROM According to ModBus (or Canbus, later)
void EEPROM_Check() {
// Just update every 5sec
if (lastEEPROMUpdate + 5000 <= millis()) {
lastEEPROMUpdate = millis();
// MaxSpeed
if (motorMaxSpeed != EEPROM_READ(EEPROM_MaxSpeed)) {
EEPROM_WRITE(EEPROM_MaxSpeed, motorMaxSpeed);
Serial.println("EEPROM Max Speed Updated");
}
// Max Stem Turns
if (motorMaxStemTurns != EEPROM_READ(EEPROM_MaxTurns)) {
EEPROM_WRITE(EEPROM_MaxTurns, motorMaxStemTurns);
Serial.println("EEPROM Max Stem Turns Updated");
}
}
}
void updateTime() {
DateTime now = rtc.now();
TimeNow = now.unixtime();
Hi = TimeNow & 0xFFFF0000;
Lo = TimeNow & 0x0000FFFF;
Serial.print(Hi);
Serial.println("Hi UpdateTime");
Serial.print(Lo);
Serial.println("Lo UpdateTime");
ModbusRTUServer.holdingRegisterWrite(2007, Hi);
ModbusRTUServer.holdingRegisterWrite(2008, Lo);
ModbusRTUServer.inputRegisterWrite(26, TimeNow);
}
void setupTime(int unix) {
rtc.adjust(DateTime(unix));
Serial.print(TimeNow);
Serial.println("SetupTime");
checkTime = false;
EEPROM_WRITE(EEPROM_TimeSet, 1);
}
// See if new data has arrived in 2007 and 2008 holding
void timeCheck() {
if (ModbusRTUServer.holdingRegisterRead(2007) > 0) {
Serial.println(ModbusRTUServer.holdingRegisterRead(2007));
Serial.println(ModbusRTUServer.holdingRegisterRead(2008));
TimeNow = (ModbusRTUServer.holdingRegisterRead(2007) << 16) | ModbusRTUServer.holdingRegisterRead(2008);
Serial.print(TimeNow);
Serial.println("TimeCheck");
setupTime(TimeNow);
Serial.println("Time is set, Sir");
}
}
// Delete Flash data
void CleanSlateProtocol(){
int i = 0;
SD.remove(EEPROM_TimeSet);
SD.remove(EEPROM_MaxSpeed);
SD.remove(EEPROM_MaxTurns);
SD.remove(EEPROM_ModbusID);
rtc.adjust(DateTime(i));
initEEPROM();
Serial.println("Clean Slate Protocol is complete, Sir");
}
// If fresh card or reset
void initEEPROM(){
// Check if time excists in EEPROM
if (!SD.exists(EEPROM_TimeSet)) { EEPROM_WRITE(EEPROM_TimeSet, 0); Serial.println("FRESH SD CARD"); }
// Check if modbusID is stored, else make new
if (!SD.exists(EEPROM_ModbusID)) { EEPROM_WRITE(EEPROM_ModbusID, 240); Serial.println("FRESH SD CARD"); }
// myStepper info that needs to be in EEPROM. If SD card is fresh, generate new files, else ignore.
if (!SD.exists(EEPROM_MaxSpeed)) { EEPROM_WRITE(EEPROM_MaxSpeed, 10); Serial.println("FRESH SD CARD"); }
if (!SD.exists(EEPROM_MaxTurns)) { EEPROM_WRITE(EEPROM_MaxTurns, 10); Serial.println("FRESH SD CARD"); }
}
// MOVE MOTOR
void stepperMove() {
// Get information and calculate the new position to move too
// if the new commanded position is higher than 100%, set it to 100%. Lower than 0%, set to 0%
if (commandedPos > 100) {
commandedPos = 100;
}
if (commandedPos < 0) {
commandedPos = 0;
}
float newPos = (commandedPos - currentPos) / 100 * motorMaxStemTurns * stepsPerRevolution;
Serial.print("Moving Motor to new pos: ");
Serial.println(commandedPos - currentPos);
myStepper.setSpeed(motorMaxSpeed);
Serial.println(motorMaxSpeed);
myStepper.step(newPos);
Serial.println(newPos);
currentPos = commandedPos;
digitalWrite(5, LOW);
digitalWrite(2, LOW);
digitalWrite(1, LOW);
digitalWrite(0, LOW);
// calc the pos and speed
// speed = maxspeed
// pos update time = new pos in turns / speed = time it takes * ms(1000) = total time / 10 = update frequency
// pos % to updat each thime (above) = newPos / 10
// set speed to 0
// set pos to new pos
// set % to new
// currentPos = commandedPos;
}
Kind regards
Amish