Last year I was doing some research and i wanted to record the Temperature and humidity (DHT22) in 22 compartments. This was in a factory with lots of steel and concrete and noise spread over a 500 ft radius. Wifi didn't look real feasible. So I opted for a wired network, RS485 and modbus protocol. There are modbus simulators that you can check the slave with from a PC. http://www.modbustools.com/modbus_poll.html . I actually used php to write a program to gather the data into a MySQL database every 5 minutes. Never got implemented yet at the factory but I do have 7 stations running full time at home . Don't get bothered by the BME280 stuff, i am just recording some data and this demonstrates how to add data to the outbuffer.
'ModbusRTUSlave'Onlytwocommands3readregand16modreg'command3readsfromthevariablearray"outbuffer(20)"whichisramwithsensordatapluggedin'command16writestoRAMandisgoingtobeusedforlimits'Adopted/severelymodifiedfromAndrzejSokulski,'andrzej@modbus.pl-www.modbus.pl'Allrightsreserved.'RewrittenbyMikeotteDecember2016/2017'Allrightsreserved.'Thissoftwareisfreeyoucanredistributeitand/or'modifyitunderthetermsoftheGNULesserGeneralPublic'LicenseaspublishedbytheFreeSoftwareFoundation;either'version2.1oftheLicense,or(atyouroption)anylaterversion.'*'Thissoftwareisdistributedinthehopethatitwillbeuseful,''butWITHOUTANYWARRANTY;'Innoeventshalltheregentsorcontributorsbeliableforanydirect,'indirect,incidential,special,exemplary,orconsequentialdamages.'ModbusslavebasedonPIC16F886PIC16F1705'Thisslavesupportsmodbusfunction3(readholdingregisters)and16(presetmultipleregisters)'command3readsfromthevariablearray"outbuffer(20)"whichisram'command16writestoEEPROMandisgoingtobeusedforlimits'TheRS232issettobaudrate-9600,databits-8,stopbits1,parity-none'ThehardwareUSARTandMSSP/I2Caredefinedbelow'TheTimer2isusedfordetectingendofmodbusframe'MasterQuery'readregisters40000–40008fromslavedevice17:'Example=$0A$03$00$00$00$08$45$77'FieldName(Hex)'SlaveAddress$0A'Function$03'StartingAddressHi$00'StartingAddressLo$00'No.ofPointsHi$00'No.ofPointsLo$08'ErrorCheck(CRC)$45––16bits=lasttwobytesofpacket'$77'SlaveRESPONSE'0A0310018300D1000567C0000804F0841BEFDF133E'ExampleASCIIRTU'FieldName(Hex)Characters8–BitField'Header:(colon)None'SlaveAddress$0A0A00001010'Function$030300000011'ByteCount$101000010000'DataHi$010100000001'DataLo$838310000011'DataHi$000000000000'DataLo$D1D111010001'.....'DataHi$EFEF11101111'DataLo$DFDF11011111'ErrorCheck$131300010011LRC(2chars.)CRC(16bits)'CRC$3E3E00111110'TrailerforasciiCRLF'TotalBytes:2511ASCIImodetakesmanymorechars'ThiscodeonlyimplementsRTU!'PresetMultipleHoldingRegisters'***********Function16Hex$10''FieldName(Hex)'SlaveAddress$10'Function$10'StartingAddressHi$00'StartingAddressLo$01'No.ofRegistersHi$00'No.ofRegistersLo$02'ByteCount$04'DataHi$00'DataLo$0A'DataHi$01'DataLo$02'ErrorCheck$__(LRCorCRC)––twobytes'$__'Responsefromslave'FieldName(Hex)'SlaveAddress$11'Function$10'StartingAddressHi$00'StartingAddressLo$01'No.ofRegistersHi$00'No.ofRegistersLo$02'ErrorCheck$__(LRCorCRC)––twobytes'$__;-----Configuration#chip 16F1705,8#option explicit#config WDT_OFF, LVP_OFF'GeneratedbyPICPPSToolforGreatCowBasic'PPSToolversion:0.0.5.11'PinManagerdata:v1.55''Templatecommentatthestartoftheconfigfile'#startup InitPPS, 85SubInitPPSUNLOCKPPS'Module:EUSARTRXPPS=0x0005'RA5>RXRC5PPS=0x0014'TX>RC5'Module:MSSPRC0PPS=0x0010'SCL>RC0SSPCLKPPS=0x0010'RC0>SCL(bi-directional)RC1PPS=0x0011'SDA>RC1SSPDATPPS=0x0011'RC1>SDA(bi-directional)LOCKPPSEndSub'Templatecommentattheendoftheconfigfile#include<BME280.h >#include<DHT.h >;-----DefineHardwaresettings'DefineI2Csettings-CHANGEPORTSifrequired#define HI2C_BAUD_RATE 400#define HI2C_DATA PORTC.1#define HI2C_CLOCK PORTC.0'InitialiseI2CSlave'I2CpinsneedtobeinputforSSPmoduleDirHI2C_DATAinDirHI2C_CLOCKin'MASTERHI2CModeMaster'DHT22HumidityandTemperature#DEFINE DHT_TYPE 22#DEFINE DHT_PIN PORTA.2'#definesensorPortA.2'PortA.0;RH/TempsensoronpindirPORTA.2in'sensorin'InitUSARTusuartisonaRS485network#define USART_BAUD_RATE 9600#define USART_BLOCKING#define MaxDir PortA.4 'RS485 direction Max485 chipdirPortA.4Out'MAX485directioncontrol'timer2determinestheendoftheframepr2=0Inittimer2PS2_16,0'Prescale1:16/Postscale1:1Starttimer2OnInterruptUsartRX1ReadyCallSerialInterruptOnInterruptTimer2MatchCallTimer2Interrupt;-----VariablesDimSlaveAddressasbyteSlaveAddress=10'Modbusslaveadress(1..255)dimDHT_rh,DHT_cels,DHT_fahrasintegerdimDHT_errorasByteDimbuffer(74)'Modbusframebuffer-changeifwanttogetmorethan32registersinsingleframeDimoutbuffer(50)'buffertocollectdatatosendbackbeforetransferingtobufferDimADC_P,ADC_TasLong'variablesusedbyBME280_Read_TPHDimADC_HasWordDimmbB0asbyte'VariablesdefinitionDimmbB1asbyte'transmitthisbyteDimmbW0asword'addresspointerDimMBFrameasbyteDimNewFrameasbyteDimLengthasbyteDimTMR2Ticksasbyte'usedtodetermineendofRTUframeDimmbiasbyte'forloopindexDimcounter1asword'wastetimecountertodonothingDimcounter2asword'wastetimecountertodonothingDimGeneratorasword'CRCvariable/constant40961DimTempaswordDimCRCasword'CRCresultDimmbjasbyte'forloopindexDimmbBitasbit'tempbitvarTMR2Ticks=0Length=0wait500msmainloop:Forcounter2=0to5'ThemainprogramForcounter1=0to10000'Writeyourcodeherenextcounter1nextcounter2'diagnostic-usedfortesting'MaxDir=1'hserprint"I am here "'MaxDir=0'MaxDir=1'hserprintLength'MaxDir=0'ModbusprotocolfunctionIfNewFrame=1Then'CheckifnewmodbusframeisinbufferNewFrame=0GosubcheckMB'CheckthemodbusReceivedframeSelectCaseMBFramecase3'MaxDir=1'hserprint"case 3 "'MaxDir=0mbReadRegResponse'Responseforfunction3case16mbwriteRegResponse'Responseforfunction16caseelsembwriteBadRequest'ResponseforerrorendselectLength=0EndifGotomainloopSubcharoutxmty:'USARTsendsinglebyteIfTXIF=0Thengotoxmty'WaitfortransmitregisteremptyTXREG=mbB1'SendcharactertotransmitregisterEndSubSubcrc__16'crc__16:'FunctiontocalculateCRC16checksumCRC=65535'CRCiswordandissettoall1'stostartGenerator=40961Formbi=1ToLengthTemp=buffer(mbi)CRC=CRC#Temp''XORedFormbj=1To8STATUS.C=0RotateCRCright'CRC=CRC>>1mbBit=STATUS.CIfmbBitThenCRC=CRC#Generator'XORedEndIfNextNext'crc16(2)=CRC/256'CRC16highbyte'crc16(1)=SysCalcTempX'CRC16lowbyte'CRCcontainstheCRCword16bitEndSubSubcheckMB'checkMB:'Checkthemodbusframeif(buffer(1)<>SlaveAddress)then'WrongSlaveaddressMBFrame=0ExitSubendifif(buffer(2)<>3)AND(buffer(2)<>16)then'WrongModbusfunctionMBFrame=0x81ExitSubendif'hserprint"wrong"'if((buffer(5)>0)OR(buffer(6)>32))then'Toomanyregisters'MBFrame=0x83'ExitSub'endif'hserprint"Toomanyreg"if(Length>74)then'ToomanybytesinframeMBFrame=0x82ExitSubendif'hserprint"len74"if(Length<9)then'FrametooshortMBFrame=0x81ExitSubendif'CheckCRCLength=Length-3crc__16'diagnosticCRCvalues(1)'MaxDir=1'hserprintcrc'MaxDir=0if(buffer(Length+1)<>[byte]CRC)OR(buffer(Length+2)<>CRC_H)then'BadCRC16checksumMBFrame=0elseMBFrame=buffer(2)'FrameOK!EndifEndSubSubmbReadRegResponse'readRegResponse:'Responseformodbusfunction3(ReadHoldingRegisters)'UPDATERegistersfirst'DHT22fillsoutbuffer1-4arewrittenintheDHT22subreadDHT(DHT_rh,DHT_cels,DHT_fahr,DHT_error)outbuffer(1)=DHT_rh_H'Writedatatobufferoutbuffer(2)=DHT_rh'RememberHoldingRegistersaskedbytthemasterare16bitoutbuffer(3)=DHT_cels_H'sothatistwobytesoutbuffer(4)=DHT_celsBME280_Read_TPHoutbuffer(5)=adc_P_E'Writedatatobufferoutbuffer(6)=adc_P_Uoutbuffer(7)=adc_P_Houtbuffer(8)=adc_Poutbuffer(9)=adc_T_E'Writedatatobufferoutbuffer(10)=adc_T_Uoutbuffer(11)=adc_T_Houtbuffer(12)=adc_Toutbuffer(13)=adc_H_Houtbuffer(14)=adc_H'outbufferis50bytesofwhichweonlyfilled14sofarmbW0=buffer(3)*255+buffer(4)'Startingaddress'mbW0=mbW0*2mbW0=mbW0-40000'40000issecretMODBUSoffsetforholdingregistersbuffer(3)=buffer(6)*2'buffer(6)isnumofpoints(words)LOwhichisgettingbufferreadyforTx'buffer(3)isthenumberofdatabytestoxfer'HeaderneadsMBaddressbyte,MBcommandbyte,NumberofbytestoXferintheheader'theyarealreadythereformbB0=1tobuffer(3)buffer(mbB0+3)=outbuffer(mbB0)'Putdatainbufferfromram,leaveroomforheaderNextLength=buffer(3)+3'Buffer(3)=Lengthcrc__16buffer(Length+1)=[byte]CRC'crc16(1)'CalculatetheCRC16forresponseframebuffer(Length+2)=CRC_H'crc16(2)Length=Length+4MaxDir=1'MAX485directioncontrol=TxFormbB0=1ToLength'SendtheresponsetoMastermbB1=buffer(mbB0)GosubcharoutNextMaxDir=0'MAX485directioncontrol+RxEndsubSubmbwriteRegResponse'writeRegResponse'Responseformodbus16function(PresetMultipleRegisters)''SlaveAddress$0A'buffer(1)'Function$10'buffer(2)'StartingAddressHi$00'buffer(3)'StartingAddressLo$01'buffer(4)'No.ofRegistersHi$00'buffer(5)'No.ofRegistersLo$02'buffer(6)'ByteCount$04'buffer(7)'DataHi$00'DataLo$0A'DataHi$01'DataLo$02'ErrorCheck$__(LRCorCRC)––twobytes'$__mbW0=buffer(3)*255+buffer(4)'Calculatethestartingaddressofregisters(Holdingregistersare16bit)mbW0=(mbW0*2)-1'Claculatestartingmemorybytetowriteto....2bytesperholdingregisterformbB0=0TO(buffer(7)-1)'buffer(7)containsthebytecountmbB1=buffer(mbB0+8)'Getbyteofdata'EPWritembW0,mbB1'WritedatatoEEPROMmemoryoutbuffer(mbW0)=mbB1'writedatatomemorymbW0=mbW0+1'incrementmemorypointer'wait10msNEXT'Responsefromslave'FieldName(Hex)'SlaveAddress$0Aallreadyinbuffer(1)'Function$10allreadyinbuffer(2)'StartingAddressHi$00allreadyinbuffer(3)'StartingAddressLo$01allreadyinbuffer(4)'No.ofRegistersHi$00allreadyinbuffer(5)'No.ofRegistersLo$02allreadyinbuffer(6)'ErrorCheck$__(LRCorCRC)––twobytes'$__Length=6'Tellcrchowmanytocalccrc__16'CalculatetheCRC16forresponseframe'CRCfieldisappendedtothemessageasthelastfieldinthemessage.'thelow–orderbyteofthefieldisappendedfirst,followedbythe'high–orderbyte.buffer(length+1)=[byte]CRC'crc16(1)buffer(length+2)=CRC_H'crc16(2)Length=Length+4MaxDir=1FormbB0=1ToLength'SendtheresponsetoMastermbB1=buffer(mbB0)GosubcharoutNextMaxDir=0EndSubSubmbwriteBadRequest'writeBadRequest:'Responseforerrorinmodbuspoll'thefollowingmessageissentasasciiandyoucanseewhatthecrcofthemastershouldbe'putthedecimalvalueintheprogrammerscalculator,switchtohex,andreversethetwobytes'remembercrcislowbytefirst,highbytesecondintheframepacketMaxDir=1hserprint"bad crc should be = "hserprintcrcHSerPrintCRLFMaxDir=0if(MBFrame<>0)then'IfMBFrame=0thennoresponsebuffer(2)=buffer(1)+0x80'Settheerrorcodeinmodbusframebuffer(3)=MBFrame-0x80'SettheerrornrLength=3crc__16buffer(length+1)=[byte]CRC'crc16(1)'CalculatetheCRC16forresponseframebuffer(length+2)=CRC_H'crc16(2)Length=Length+4MaxDir=1FormbB0=1ToLength'SendtheresponsetoMastermbB1=buffer(mbB0)GosubcharoutNextmbB0MaxDir=0EndifEndSub'readMB:SubSerialInterruptIfOERRthen'OERRorFERRRC1STA.2IfUSARTerrorthencleartheerrorflagCREN=0'CRENendifIfFERRthen'OERRorFERRRC1STA.2IfUSARTerrorthencleartheerrorflagCREN=0'CRENendifCREN=1ifRCIFthen'RCIFUSARTRECInterrupt(PIR1.5=1)TMR2Ticks=0'resettimeoutwhichindicatesendofMBFrameIf(NewFrame=1)OR(Length=0)then'NewmodbusframestartNewFrame=0T2CON.2=1'EnableTimer2Length=1EndIfdowhileRCIF'Writemodbusframetobuffer(PIR1.5=1)buffer(Length)=RCREGLength=Length+1ifLength=75thenLength=0EndIfloopEndIfEndsubSubTimer2Interruptif(TMR2IF=1)then'Timer2interruptflagTMR2IF=0TMR2ON=0'Timer2disableTMR2Ticks=TMR2Ticks+1IfTMR2Ticks>120then'~10mswithoutnewcharNewFrame=1'TherewasnonewcharonUSART=>endofmodbusframeelseTMR2ON=1'EnabletimerEndIfEndIf'ResumeEndSub
This is only RTU implementation not ascii. Only used HSer routines for developement and trouble shooting (some are left). It was easier to read and write to the USART. Modbus is a Master /Slave protocol. The Master asks for data or sends data and tells the slave where to put it. The commands sent to the slave and the data sent back from the slave are in packets starting with the slaves address and ending with a CRC16 check. I included some modbus documentation.
GL
Mike
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
about the code for modbus 3 and 16....I would like to use a PLC as master and a glcd as slave.....what modifications must do to above code modbus RTU slave, to work with PIC16F1939 ? the pic that work as slave is connected to a glcd, to diplay text that I send from PLC (modbus master)...so, what is the variable (Rx) that will checked for incoming data in above code ? the above code uses var outbuffer ....below is my first approach to slave code ....the code for writing to glcd is ok....just to setup pic uart for RS-485 and check for incoming ..
modbus slave ........initialize
glcd ......initialize
var( Rx)= modbus slave : check for incoming
locate glcd cursor ...... var ( Idx = total pixels of glcd ).....go to the begging of row
read holding register value.......var (pic_reg)=modbus slave: read holding reg (Idx)
print value to glcd......print (pic_reg)
modbus error loop .....if Rx=255 then output 1 to a pin .
an opinion for how to add the above code to glcd code...
thanks in advance,
Basil
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Basil,
First question is who is Master and who is slave. Who initiates the communication? Modbus Master sends the command and the slave sends the response. In my experience the slave was the PLC and my Distributed ControlSytem would send and receive from the Holding registers of the PLC thus making the DCS the master.
If you can make your PLC act as Master, then that is fine because everything you need is in the code for Modbus 3 and 16. Your PLC would send the command 16 (modbus 16 function (Preset Multiple Registers)) to the PIC with the data and you would simulate holding registers in the PIC . Remember the holing registers are 16bit words. Then you LCD part of the slave program would write it to the LCD.
mbB1 = buffer(mbB0 + 8) ' getting data from input buffer
EPWrite mbW0,mbB1 ' Write data to EEPROM memory 'writing data to holding registers or you LCD
Then the slave sends back the response confirming the transaction.. Header +the number of bytes and CRC16.
What brand and model PLC are you using?
I will help you through this.
Mike
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Basil,
I tried to comment and answer your questions in the above message.
"what modifications must do to above code modbus RTU slave, to work with PIC16F1939 ?"
Change the #chip 16F1939 ,8
define port for max485 direction
define usart port pins
"the pic that work as slave is connected to a glcd, to diplay text that I send from PLC (modbus master)..."
I am assuming you know how and where to make the PLC Master
"so, what is the variable (Rx) that will checked for incoming data in above code ?"
The master will send a command and it will be received in the " buffer()", It comes in as a data packet and when
the TMR2 times out it says the "Frame" Is received, Then the frame(all the bytes are in the buffer) is checked for address,
command, number of bytes, crc16 and then the command is executed. In your case ,Command 16, you would put the bytes
where you can use them for LCD display, Then a packet is sent back confirming reception.
"the above code uses var outbuffer"
outbuffer has nothing to do with the Modbus code, It is part of my DHT22 code only
"....below is my first approach to slave code ....the code for writing to glcd is ok...."
"just to setup pic uart for RS-485 and check for incoming .. modbus slave ........initialize"
usart baud blocking
set up pins tx, rx, dir
setup interrupts
"glcd ......initialize"
yours
"var( Rx)= modbus slave : check for incoming"
automatically comes in when frame is received complete
"locate glcd cursor ...... var ( Idx = total pixels of glcd ).....go to the begging of row"
what are doing with pixels?
"read holding register value.......var (pic_reg)=modbus slave: read holding reg (Idx)"
Your data is in the buffer and will be sent to your buffer with "Sub mbwriteRegResponse"
which processes the command 16
"print value to glcd......print (pic_reg)"
Your displaying the data
"modbus error loop .....if Rx=255 then output 1 to a pin ."
If errors occur then Modbus sends error message back to the master.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Mike,
many thanks for your detailed answer...
The PLC is Schneider Modicon M221... PLC configured as master and the board with pic-glcd is the slave....from PLC we send a text that displayed to glcd...the communication starts from PLC that send commands and the slave responds.. .because my knowledge about GCB is limited , would you like to help me to merge the two codes?....Anobium has be done an excellent work to help me, and sends to me a code to write characters to a glcd with a pic(PIC16F1939)....I send you that demo code to add if it possible the part of modbus slave code....for now instead of master ( i'll get Modicon later ) I use ComTestPro as modbus master simulator to send commands to slave board.....is it possible to send larger characters to glcd, or ghraphics (bmp) ? for any other info please let me know...
thanks in advance.
Basil
Mike,
I made a modification to code , to display some text...just to test the code....I send you the modified code.... as glcd I use a 240x128 type AGM2412A ( DISPLAYTRONIC) with T6963C...for the hardware I use a PIC16F1939 , an RS-485 connector , an SN75176 as receiver/transmitter for modbus data, the glcd as above ....( If you need schematic please let me know), the connections pic-glcd are : port B to D0-D7 of glcd , RW to pin D.3 , RD to pin C.4, CE to pin C.5, CD to pin D.5, RESET to pin D.6 , FS to pin A.0........connections SN75176 to pic..: pin 1( R) to C.7 pin (RX)..pin 4(D) to pin C.6 (TX)...direction pin ( RE, DE) to pin A.1....
Basil,
This is what I had last night at 11pm.
It wouldn't compile for me because of LCD errors.... I am running 98.03GCB and don't have your GLCD in my library. There are more things I want to change in the interrupts but this is close to running.
Mike,
very good work ...it compile for me, so can I send some characters with ComTestPro to test? is not critical for me the same type of glcd or pic...any type of glcd 240x128 even and 240x64 can be used ...Anobium make the code pic-glcd using a glcd 240x64....with a modification easy can work with 240x128....only to have controller T6963 or similar....I'll make some tests at next hours and inform you about....
best regards
Basil
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Mike,
I made some tests to send characters with ComTestPro...I had no response...I send 10 chars with mb command 16...as it seems at log there is no response with time out error...I was checked hardware for errors...ok...
Basil,
I am working to put together a test board so I can get immediate feedback and trouble shoot.
What you sent looks correct. The time out could be the time out setting on comtestpro is 100 ms and had the scan really slow at 4sec
do Forever
wait 4 s
' Modbus protocol function
Try increasing the comtestprotime out to 5000ms Or turn the wait down to something much lower. This would not be a solution but a test.
I probably need to take the "' Modbus protocol function" out and trigger it by itself. When a frame is complete.
I will work on this as I get time. I have to go to town today.
BR
Mike
I moved the processing of the Frame out of the main loop! See attached. This is a severely revised Version that I was working on last night. Worth a try.
Last edit: mmotte 2019-02-15
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Basil,
The time out is beacuse of the setting on the comtestpro timeout in upper right corner 100ms and I had put the processing of the new fram in the display main loop which cycles at 4000ms.
I moved the processing of new frame to the interrupt of the mbtimer2match. this is not ideal but worth a try.
working at putting some hardware together so i can test here
Mike,
after change the mb slave address to 07 , I send 10 chars (ABCDEFGHIJ) , I had response error (time out) , but I had the chars on glcd as below ..
Last year I was doing some research and i wanted to record the Temperature and humidity (DHT22) in 22 compartments. This was in a factory with lots of steel and concrete and noise spread over a 500 ft radius. Wifi didn't look real feasible. So I opted for a wired network, RS485 and modbus protocol. There are modbus simulators that you can check the slave with from a PC. http://www.modbustools.com/modbus_poll.html . I actually used php to write a program to gather the data into a MySQL database every 5 minutes. Never got implemented yet at the factory but I do have 7 stations running full time at home . Don't get bothered by the BME280 stuff, i am just recording some data and this demonstrates how to add data to the outbuffer.
This is only RTU implementation not ascii. Only used HSer routines for developement and trouble shooting (some are left). It was easier to read and write to the USART. Modbus is a Master /Slave protocol. The Master asks for data or sends data and tells the slave where to put it. The commands sent to the slave and the data sent back from the slave are in packets starting with the slaves address and ending with a CRC16 check. I included some modbus documentation.
GL
Mike
Very nice code. Excellent work.
Do have any photos of the opertional solution?
I do have a few pics. Should I post them here or send them via other method? i will post one now
Wonderful - can you send to Bed? And, I can we publish on the WebSite with some nice words?
Took the challenge.
Thanks for sharing your work
Article is online, now.
http://gcbasic.sourceforge.net/Typesetter/index.php/Modbus-Slave
Mike,
about the code for modbus 3 and 16....I would like to use a PLC as master and a glcd as slave.....what modifications must do to above code modbus RTU slave, to work with PIC16F1939 ? the pic that work as slave is connected to a glcd, to diplay text that I send from PLC (modbus master)...so, what is the variable (Rx) that will checked for incoming data in above code ? the above code uses var outbuffer ....below is my first approach to slave code ....the code for writing to glcd is ok....just to setup pic uart for RS-485 and check for incoming ..
modbus slave ........initialize
glcd ......initialize
var( Rx)= modbus slave : check for incoming
locate glcd cursor ...... var ( Idx = total pixels of glcd ).....go to the begging of row
read holding register value.......var (pic_reg)=modbus slave: read holding reg (Idx)
print value to glcd......print (pic_reg)
modbus error loop .....if Rx=255 then output 1 to a pin .
an opinion for how to add the above code to glcd code...
thanks in advance,
Basil
Basil,
First question is who is Master and who is slave. Who initiates the communication? Modbus Master sends the command and the slave sends the response. In my experience the slave was the PLC and my Distributed ControlSytem would send and receive from the Holding registers of the PLC thus making the DCS the master.
If you can make your PLC act as Master, then that is fine because everything you need is in the code for Modbus 3 and 16. Your PLC would send the command 16 (modbus 16 function (Preset Multiple Registers)) to the PIC with the data and you would simulate holding registers in the PIC . Remember the holing registers are 16bit words. Then you LCD part of the slave program would write it to the LCD.
Then the slave sends back the response confirming the transaction.. Header +the number of bytes and CRC16.
What brand and model PLC are you using?
I will help you through this.
Mike
Basil,
I tried to comment and answer your questions in the above message.
"what modifications must do to above code modbus RTU slave, to work with PIC16F1939 ?"
Change the #chip 16F1939 ,8
define port for max485 direction
define usart port pins
"the pic that work as slave is connected to a glcd, to diplay text that I send from PLC (modbus master)..."
I am assuming you know how and where to make the PLC Master
"so, what is the variable (Rx) that will checked for incoming data in above code ?"
The master will send a command and it will be received in the " buffer()", It comes in as a data packet and when
the TMR2 times out it says the "Frame" Is received, Then the frame(all the bytes are in the buffer) is checked for address,
command, number of bytes, crc16 and then the command is executed. In your case ,Command 16, you would put the bytes
where you can use them for LCD display, Then a packet is sent back confirming reception.
"the above code uses var outbuffer"
outbuffer has nothing to do with the Modbus code, It is part of my DHT22 code only
"....below is my first approach to slave code ....the code for writing to glcd is ok...."
"just to setup pic uart for RS-485 and check for incoming .. modbus slave ........initialize"
usart baud blocking
set up pins tx, rx, dir
setup interrupts
"glcd ......initialize"
yours
"var( Rx)= modbus slave : check for incoming"
automatically comes in when frame is received complete
"locate glcd cursor ...... var ( Idx = total pixels of glcd ).....go to the begging of row"
what are doing with pixels?
"read holding register value.......var (pic_reg)=modbus slave: read holding reg (Idx)"
Your data is in the buffer and will be sent to your buffer with "Sub mbwriteRegResponse"
which processes the command 16
"print value to glcd......print (pic_reg)"
Your displaying the data
"modbus error loop .....if Rx=255 then output 1 to a pin ."
If errors occur then Modbus sends error message back to the master.
Mike,
many thanks for your detailed answer...
The PLC is Schneider Modicon M221... PLC configured as master and the board with pic-glcd is the slave....from PLC we send a text that displayed to glcd...the communication starts from PLC that send commands and the slave responds.. .because my knowledge about GCB is limited , would you like to help me to merge the two codes?....Anobium has be done an excellent work to help me, and sends to me a code to write characters to a glcd with a pic(PIC16F1939)....I send you that demo code to add if it possible the part of modbus slave code....for now instead of master ( i'll get Modicon later ) I use ComTestPro as modbus master simulator to send commands to slave board.....is it possible to send larger characters to glcd, or ghraphics (bmp) ? for any other info please let me know...
thanks in advance.
Basil
Basil,
Good information.
Are you making a message display or aiming for a HMI? Could you give a typical message?
I will look this over.
Mike,
I made a modification to code , to display some text...just to test the code....I send you the modified code.... as glcd I use a 240x128 type AGM2412A ( DISPLAYTRONIC) with T6963C...for the hardware I use a PIC16F1939 , an RS-485 connector , an SN75176 as receiver/transmitter for modbus data, the glcd as above ....( If you need schematic please let me know), the connections pic-glcd are : port B to D0-D7 of glcd , RW to pin D.3 , RD to pin C.4, CE to pin C.5, CD to pin D.5, RESET to pin D.6 , FS to pin A.0........connections SN75176 to pic..: pin 1( R) to C.7 pin (RX)..pin 4(D) to pin C.6 (TX)...direction pin ( RE, DE) to pin A.1....
Mike,
I send you a short video to see glcd with pcb that I was designed as modbus monitor....
Basil,
This is what I had last night at 11pm.
It wouldn't compile for me because of LCD errors.... I am running 98.03GCB and don't have your GLCD in my library. There are more things I want to change in the interrupts but this is close to running.
I don't have your GLCD nor the PIC for a test bed
GL
Mike
Mike,
very good work ...it compile for me, so can I send some characters with ComTestPro to test? is not critical for me the same type of glcd or pic...any type of glcd 240x128 even and 240x64 can be used ...Anobium make the code pic-glcd using a glcd 240x64....with a modification easy can work with 240x128....only to have controller T6963 or similar....I'll make some tests at next hours and inform you about....
best regards
Basil
Mike,
I made some tests to send characters with ComTestPro...I had no response...I send 10 chars with mb command 16...as it seems at log there is no response with time out error...I was checked hardware for errors...ok...
Mike,
the glcd screen after power on before send charcters..no change at tests...
Basil,
I am working to put together a test board so I can get immediate feedback and trouble shoot.
What you sent looks correct. The time out could be the time out setting on comtestpro is 100 ms and had the scan really slow at 4sec
Try increasing the comtestprotime out to 5000ms Or turn the wait down to something much lower. This would not be a solution but a test.
I probably need to take the "' Modbus protocol function" out and trigger it by itself. When a frame is complete.
I will work on this as I get time. I have to go to town today.
BR
Mike
I moved the processing of the Frame out of the main loop! See attached. This is a severely revised Version that I was working on last night. Worth a try.
Last edit: mmotte 2019-02-15
Mike,
I changed slave address to 07 to mb master according to code....the same ...no response...
Basil,
The time out is beacuse of the setting on the comtestpro timeout in upper right corner 100ms and I had put the processing of the new fram in the display main loop which cycles at 4000ms.
I moved the processing of new frame to the interrupt of the mbtimer2match. this is not ideal but worth a try.
working at putting some hardware together so i can test here
BR
Mike
Mike,
after change the mb slave address to 07 , I send 10 chars (ABCDEFGHIJ) , I had response error (time out) , but I had the chars on glcd as below ..
Mike,
but in the next cycles chars changed as below.... I will try the updated code in a few minutes...
That's exciting! We're close!
Mike,
test for V1.55....the same as previous...responce error (time out) , 10 chars ok, but the first 4 chars changed in the next cycles....
Mike,
hoping to help, I send you a code in C (for FC7), that works fine with a lcd 4x20 with modbus protocol...