[ SOLVED ] Trying to add modbus to serialcommand??

Post here all questions related to LibMaple core if you can't find a relevant section!
Post Reply
Koep
Posts: 10
Joined: Sat Sep 12, 2020 10:24 am

[ SOLVED ] Trying to add modbus to serialcommand??

Post by Koep »

Hi All!



I have so far been using this https://github.com/kroimon/Arduino-SerialCommand for my comms with success.

Now I need to add ModBus to my project so that I can pass messages to Mach3.

I found the sketch (plain modbus)at the end of this post online. It reads the message from Mach and sends response
which is read by Mach giving expected results.

However I want to keep the ability to receive serial (ASCII) messages already implemented so I modified the
SerialCommand library to also respond to modbus message.

I changed line 118 to line 119 and added lines 131,132 and 133 as per snippet below.
I added the first 3 lines and the Reply_Inputs() function from plain modbus to my sketch

My Setup is win7 with Roger's core 1.6.9IDE and 2 bluepills connected through a USB hub.

The plain modbus sketch works on both pills.

Using this: https://www.hhdsoftware.com/device-monitoring-studio

I can see both the request "0x01 0x04 0x00 0x00 0x00 0x31 0xca" from Mach and the response

The response is the same also when I use realterm to send the request (in hex)
https://realterm.sourceforge.io/

Now my actual issue/problem:

Using realterm my skecth sends out the correct response verifiable by both realterm and devicemonitoringstudio
but using Mach my skecth does not respond anything??

There is still 2 differences that I can see:

1 buffer data type int vs char

2 if ( Serial.available() == 8 ) vs while (Serial.available() > 0)

but can they be the cause, as the request is Hex and triggers response depending on which application sends it?


I know that its sort of unfair to post above question but I'm all out of ideas at the moment.

Rgds
Tapio






Code: Select all

118	//    else if (isprint(inChar)) {     // Only printable characters into the buffer  <- Changed this
119	    else if (inChar) {     // All characters into the buffer                        <- to this
120	      if (bufPos < SERIALCOMMAND_BUFFER) {
121	        buffer[bufPos++] = inChar;  // Put character into buffer
122	        buffer[bufPos] = '\0';      // Null terminate
123	      } else {
124	        #ifdef SERIALCOMMAND_DEBUG
125	          Serial.println("Line buffer is full - increase SERIALCOMMAND_BUFFER");
126	        #endif
127	      }
128	    }
129		}
130	
131	   if (buffer[0] == 0x01 & buffer[1] == 0x04) {  // Added this if statement
132	         (*commandList[0].function)();
133  		clearBuffer();
134	       }


Code: Select all

//*****start plain modbus*****

unsigned int CRC16, SEED, GP; //for CRC16
#define SEED 0xFFFF  //initialization for CRC16
#define GP   0xA001  //generating polynomial
int in_buffer[8]; // receiving buffer

#define CycleStart 2;  //MOD:0-D0
#define FeedHold 3;  //MOD:0-D1
#define StopFile 4;  //MOD:0-D2
#define SingleStep 5;  //MOD:0-D3
#define ResumeFile 6;  //MOD:0-D4
#define ZeroX 7;  //MOD:0-D5
#define ZeroY 8;  //MOD:0-D6
#define ZeroZ 9;  //MOD:0-D7


void setup()
{
  Serial.begin(115200);            // Init serial communication
  pinMode(PA0, INPUT_PULLUP);           // Set pin to input
  pinMode(PA1, INPUT_PULLUP);           // Set pin to input
  pinMode(PA2, INPUT_PULLUP);           // Set pin to input
  pinMode(PA3, INPUT_PULLUP);           // Set pin to input
  pinMode(PA4, INPUT_PULLUP);           // Set pin to input
  pinMode(PA5, INPUT_PULLUP);           // Set pin to input
  pinMode(PA6, INPUT_PULLUP);           // Set pin to input
  pinMode(PA7, INPUT_PULLUP);           // Set pin to input
}

void loop()
{
   if ( Serial.available() == 8 ) {
     
     // read ModBus command from Mach3
     for (int i=0; i < 8; i++) in_buffer[ i ] = Serial.read();
     
     if ((in_buffer[0]== 0x01)&(in_buffer[1]== 0x04))
         Reply_Inputs();
         else
         Reply_Error();
         
     Serial.flush();
 
   }   
}

/// ----------------------- Functions -----------------------
void Reply_Inputs()
{
   CRC16 = SEED;
   int Inputs[] ={0x01, 0x04, 0x0A, 0x00, 0x00,
                  0x00, 0x00, 0x00, 0x00, 0x00,
                  0x00, 0x00, 0x00, 0x00, 0x00};

   Inputs[4] = ( 
               ((!digitalRead(PA7))<<7) | 
               ((!digitalRead(PA6))<<6) | 
               ((!digitalRead(PA5))<<5) |
               ((!digitalRead(PA4))<<4) |
               ((!digitalRead(PA3))<<3) |
               ((!digitalRead(PA2))<<2) |
               ((!digitalRead(PA1))<<1) |
               ((!digitalRead(PA0))   ) );

   for (int i = 0; i < 13; i++)
        {   
        Calc_CRC(Inputs[i], &CRC16); 
        }     
        
        Inputs[13]=CRC16;
        CRC16=CRC16>>8;
        Inputs[14]=CRC16;
   
   for (int i=0; i < 15; i++) Serial.write(Inputs[i]);
}
/// ---------------------------------------------------------
void Reply_Error()
{
   int Error[] ={0x01, 0x84, 0x00, 0x43, 0x00};
   for (int i=0; i < 5; i++) Serial.write(Error[i]);
}
/// ---------------------------------------------------------
void Calc_CRC(unsigned char b, unsigned int* CRC)
{
   int carry, i;

   CRC[0] ^= b & 0xFF;
   for (i=0; i<8; i++)
   {
      carry = CRC[0] & 0x0001;
      CRC[0]>>=1;
      if (carry) CRC[0] ^= GP;
   }
}
/// ---------------------------------------------------------
Last edited by Koep on Tue Nov 23, 2021 5:37 pm, edited 1 time in total.
mrburnette
Posts: 633
Joined: Thu Dec 19, 2019 1:23 am
Answers: 7

Re: Trying to add modbus to serialcommand??

Post by mrburnette »

I must be missing something, I do not understand why the Arduino-SerialCommand lib is being used. It is very easy to deal with serial without a library and overhead. A couple of very old examples written years ago:

https://forum.arduino.cc/t/dont-cross-t ... sor/144054

https://forum.arduino.cc/t/the-qbf-the- ... ags/229468

Even GPS can easily be decoded "in real time" ... libraries are somewhat like black holes and you give them complete trust for correct behavior.

Code: Select all

/*
  GPS serial read and sentence detection
  Derrived from work by M. Ray Burnette on the PSoC code
  Compiler board: Nano
  Arduino 1.7.3 & 1..7.8 // 6/09/2015 & 1/17/2016
    Sketch uses 504 bytes (1%) of program storage space. Maximum is 30,720 bytes.
    Global variables use 16 bytes (0%) of dynamic memory, leaving 2,032 bytes for local variables. Maximum is 2,048 bytes
*/

#include <stdlib.h> // for function atoi()

// Global variables
uint8_t hour, minute, seconds;          //, year, month, day;
uint8_t day, month, year;

char    buffer[9];
char    nmea[120];

// double  atof(const char *str);       // elimited Limor's float implementation

boolean DayLightSavings;                // Pin 3.4
uint8_t GMToffset = 4;
uint8_t Flag = 1;
uint32_t  c = '\0';
int8_t  k   = 0 ;

boolean DSTime_Read(void) {
  return true;
}

void GLCD_WRITE( char *x ) {
}

void glcd_write(void) {
}

void glcd_clear(void) {
}

void glcd_tiny_draw_string(int x, int y, char* c) {
}

char UART_UartGetChar() {
  char i = '\0';
  return i;
}

void LED_Write( boolean PinState) {
}

boolean LED_Read(void) {
  return ~true;
}

void setup(void)
{
    DayLightSavings = DSTime_Read();        // Pin# Normally true (HIGH)

    if(! DayLightSavings) GMToffset += 1;
}

void loop(void)
    {
        if (Flag == 1)
        {
            GLCD_WRITE("Waiting on GPS") ;
            // Flag = 0;
        
        c = UART_UartGetChar();     // Get received character or null
        if (c)  
        {
            if(c == '$')            // $ start of NMEA sentences
            {
                for(k=0; k<5; k++)  // need 5 characters for sentence type
                {
                    LED_Write( ~LED_Read() );   // flicker LED for activity
                    do {
                        c = UART_UartGetChar();
                    }
                    while (! (c));
                    nmea[k] = c;    // G + P + R + M + C
                    // sprintf(buffer + k, "%c", c) ;  // only for debug
                }

                LED_Write( LOW );                   // LED off
                if (strstr(nmea, "GPRMC"))
                {
                    do {
                        do {
                            c = UART_UartGetChar();
                            LED_Write( ~LED_Read() ); // flicker LED
                        } while (!(c));
                        nmea[k] = c;
                        ++k;
                    } while ( !( c == '*' ) && k < 120) ;   // marker
                    LED_Write( LOW );                       // LED off
                    // Inspiration: Limor Fried's Arduino GPS lib
                    char *p = nmea;
                    p = strchr(p, ',') + 1;   // position after 1st comma
                    // float    timef  = atof(p);
                    // uint32_t  time  = timef;
                    uint32_t  time  = atoi(p);
                              hour  = time / 10000;
                            minute  = (time % 10000) / 100;
                           seconds  = (time % 100);
                    // output to GLCD
                    sprintf(buffer, "              %s","");  // clearbuffer
                    
                    // this corrects time to the West but not the date!!!
                    if( hour > GMToffset) {
                        hour = hour - GMToffset;
                    } else {
                        hour = (hour + 24) - GMToffset; }

                    // correct midnight to follow standard format
                    if (hour == 24) hour = 0;

                    if (hour < 10) {
                        if (DayLightSavings) {
                            sprintf(buffer, "EDT:  %d", hour);
                        } else {
                            sprintf(buffer, "EST:  %d", hour);
                        }
                    } else {
                        if (DayLightSavings) {
                            sprintf(buffer, "EDT: %d%d", hour);
                        } else {                       
                        sprintf(buffer, "EST: %d%d", hour); }
                    }
                    if (minute < 10) {
                        sprintf(buffer + 7, ":0%d", minute);
                    } else {
                        sprintf(buffer + 7, ":%d%d", minute); }
                    if (seconds < 10) {
                        sprintf(buffer + 10,":0%d%s", seconds);
                    } else {
                        sprintf(buffer + 10, ":%d%d%s", seconds); }
                    sprintf(buffer + 13, "%s", "\0");
                    if(Flag == 1)
                    {
                        Flag = 0;
                        glcd_clear() ;
                        delay(250) ;
                    }
                    // OUTPUT the TIME on GLCD
                    glcd_tiny_draw_string(0, 4, buffer);
                    glcd_write() ;
                    // Parse to integer date field
                    p = strchr(p, ',') +1;  // A/V?
                    p = strchr(p, ',') +1;  // lat
                    p = strchr(p, ',') +1;  // N/S?
                    p = strchr(p, ',') +1;  // lon
                    p = strchr(p, ',') +1;  // E/W?
                    p = strchr(p, ',') +1;  // speed
                    p = strchr(p, ',') +1;  // angle
                    p = strchr(p, ',') +1;  // move pass for date DDMMYY
                    // nmea date field looks like: 090914 (European)
                    uint32_t fulldate = atoi(p);
                    day     = (fulldate / 10000);
                    month   = (fulldate % 10000) / 100;
                    year    = (fulldate % 100);
                    sprintf(buffer, "              %s","");  // clearbuffer
                    if (day < 10) {
                        sprintf(buffer, "0%d", day);
                    } else {
                        sprintf(buffer, "%d%d", day); }
                    if (month < 10) {
                        sprintf(buffer + 2,"-0%d", month);
                    } else {
                        sprintf(buffer + 2,"-%d%d", month); }
                    sprintf(buffer + 5, "-20%d%d%s", year);
                    sprintf(buffer + 10,"%s",'\0');
                    // OUTPUT the DATE on GLCD
                    glcd_tiny_draw_string(0, 2, buffer);
                    glcd_write() ;
                }   // if (strstr(nmea, "GPRMC"))
            }       // if(c == '$')
        }           // if(c)
      }             // flag
    }               // loop()


/* [] END OF FILE */
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: Trying to add modbus to serialcommand??

Post by ag123 »

with stm32 you have something more powerful than modbus, that bus is called USB.
all you need is Serial.print(xxyy), to communicate with the host.
mrburnette
Posts: 633
Joined: Thu Dec 19, 2019 1:23 am
Answers: 7

Re: Trying to add modbus to serialcommand??

Post by mrburnette »

ag123 wrote: Fri Nov 19, 2021 3:40 pm with stm32 you have something more powerful than modbus, that bus is called USB.
all you need is Serial.print(xxyy), to communicate with the host.
I believe Op is wishing to drive ModBus to Mach3. The interface is well documented:
http://www.homanndesigns.com/pdfs/Using ... _Mach3.pdf

I would only steer you to USB to create a hair-pulling and screaming moment :mrgreen:


Koep wrote: Thu Nov 18, 2021 5:22 pm ...
However I want to keep the ability to receive serial (ASCII) messages already implemented so I modified the
SerialCommand library to also respond to modbus message.
...
If I were attempting to do this, I think I would capture the serial print messages from within the modbus code, assuming that the serial input there can just write to a global buffer. Then after determination is made that modbus is finished or idle, call a function to inspect the global buffer, spool any ASCII messages, reset the buffer, return to modbus routine awaiting serial.

Generally, in a single-thread process, it is necessary to manage one serial stream at a time due to interrupt considerations.
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: Trying to add modbus to serialcommand??

Post by ag123 »

There are some ways to 'debug' it though, one of them is to get an st-link and setup a debug environment with it.
it takes some skills.
common tools in linux are like openocd
https://openocd.org/
and eclipse with CDT
https://www.eclipse.org/cdt/
I'm not too sure what works in windows. tips here may help
https://github.com/stm32duino/wiki/wiki/How-to-debug

A key would be that the request / response would need to be timing insensitive, since you'd be tracing codes.
This is perhaps as close as you can get to a real 'debug' as you would be stepping codes (on the bluepill) to figure out what is wrong, examine variables and registers etc.
hope it helps.

host software like Mach3 is pretty much a black box and you may need to approach them for support.
Koep
Posts: 10
Joined: Sat Sep 12, 2020 10:24 am

Re: Trying to add modbus to serialcommand??

Post by Koep »

mrburnette wrote: Fri Nov 19, 2021 11:06 pm
I believe Op is wishing to drive ModBus to Mach3. The interface is well documented:
http://www.homanndesigns.com/pdfs/Using ... _Mach3.pdf
Yes, this is my intent. I need to get info / pin states from blue pill to Mach3 and modbus is only feasible way.

mrburnette wrote: Fri Nov 19, 2021 11:06 pm If I were attempting to do this, I think I would capture the serial print messages from within the modbus code, assuming that the serial input there can just write to a global buffer. Then after determination is made that modbus is finished or idle, call a function to inspect the global buffer, spool any ASCII messages, reset the buffer, return to modbus routine awaiting serial.

Generally, in a single-thread process, it is necessary to manage one serial stream at a time due to interrupt considerations.
This is what I at least think I am doing.

Latest development is that now neither of my pills responds to request/serial message from Mach3, but both still correctly respond to message sent from realterm. :?: And device monitoring studio shows that both requests are equal so the only difference is the app used to open comm port and to send message

This came about when I uninstalled maple serial from device manager and ran the install_drivers.bat as per wiki.
mrburnette
Posts: 633
Joined: Thu Dec 19, 2019 1:23 am
Answers: 7

Re: Trying to add modbus to serialcommand??

Post by mrburnette »

Koep wrote: Sat Nov 20, 2021 8:40 pm ...
Latest development is that now neither of my pills responds to request/serial message from Mach3, but both still correctly respond to message sent from realterm. :?: And device monitoring studio shows that both requests are equal so the only difference is the app used to open comm port and to send message

This came about when I uninstalled maple serial from device manager and ran the install_drivers.bat as per wiki.
I looked around for pictorial representations of a similar setup using RS232 TTL and RS485 simultaneously and I have found very little. This came up in the search, however:
http://en.trialcommand.com/blog/arduino ... nic-rs232/

The Windows driver has always been problematic. On W7 it was acceptable, on W8.0 it was a bitch, on W8.1 it could be tamed but would often go on a tangent, and W10 with current patches is somewhat working OK.

Linux just works... all the time once permissions are correctly set.
Koep
Posts: 10
Joined: Sat Sep 12, 2020 10:24 am

Re: Trying to add modbus to serialcommand??

Post by Koep »

mrburnette wrote: Sat Nov 20, 2021 11:46 pm
The Windows driver has always been problematic. On W7 it was acceptable, on W8.0 it was a bitch, on W8.1 it could be tamed but would often go on a tangent, and W10 with current patches is somewhat working OK.

Linux just works... all the time once permissions are correctly set.
This got me thinking what if and after doing this: http://woshub.com/how-to-clean-up-and-r ... windows-7/
I got working connection to Mach3. Also the original idea of integrating modbus commands to the serialcommands library works.

Thank You all for time and interest given to my questions

Rgds,
Tapio
Post Reply

Return to “General discussion”