Seperate 32bit value to 2 16bit

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. :stuck_out_tongue:

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

  Hi = TimeNow & 0xFFFF0000;
  Lo = TimeNow & 0x0000FFFF;

How are Hi and Lo declared ?

See full code,

int Hi;
int Lo;

before the setup. as global variable.

Start by making all the variables unsigned

Try this

unsigned long TimeNow;
unsigned int Hi;
unsigned int Lo;

void setup()
{
  Serial.begin(115200);
  while (!Serial);
}

void loop()
{
  TimeNow = micros();
  Hi = (TimeNow >> 16) & 0x0000FFFF;
  Lo = TimeNow & 0x0000FFFF;
  Serial.print("TimeNiw : ");
  Serial.println(TimeNow);
  Serial.print("Hi : ");
  Serial.println(Hi);
  Serial.print("Lo : ");
  Serial.println(Lo);
  Serial.println();
  delay(2000);
}

It worked. Thank you, now i just need to do more research and figure out how.
I still dont understand 100%.

20minutes later:
Ahhaaaaaaaa. Found this interesting.
Found this, that dumbed it down for me.

bitwise operations and extracting low and high bytes from int

When you use a bitwise AND it follows the normal rules for any boolean truth table. 1 & 1 = 1, 1 & 0 = 0... Since the value is being ANDed, effectively, against 0x0000FFFF, the top two bytes will be cleared to zero while the bottom two bytes are preserved. In this way you can extract the lower two bytes out of the four byte long.

I understand, thank you UKHeliBob, you kicked me in the right direction. :bowing_man: :smiley:

Actually it was @guix who first suggested bit shifting but for some reason he has deleted his post. My example just illustrated what he had suggested

Ahh didnt catch that, i saw he was replying but never got his message.
Thanx anyway.
I didnt understand bit shifting, and didnt know what it was called, so i searched for sometime before you in here pointed me to the correct way. :smiley:

Don't forget to use unsigned variables otherwise you will get unexpected results

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.