TG9541 / stm8ef-modbus

A lightweight framework for MODBUS RTU nodes in STM8 eForth that can do more than just I/O
https://github.com/TG9541/stm8ef/wiki/Board-C0135
MIT License
31 stars 12 forks source link

New issue with modbus on STM8S103F3. #42

Closed szymonk159 closed 2 years ago

szymonk159 commented 2 years ago

@TG9541 It is me again. I don't know how to reopen last issue so I created the new one. I sucessfully downloaded STM8S001J3.ihx as you recommended on my STM8S103F3 board. Then I wanted to upload modbus protocol from STM8S001J3RS485 file using halfduplex on PD5 pin but during compilation it stops on uploading WIPE RAM at line 18 in UARTISR file and I dont know why. Firstly I uploaded BUSCTRL and console shows ok. After #include STM8S001J3RS485/board.fs I have some problems. My purpose is to download modbus protocol on my board and then add temperature sensor and read temperature by modbus. Do you have any advice? Thank you very much in advance. issue2

TG9541 commented 2 years ago

The good news is that you don't get a time-out like last time, so basically the first problem has been resolved. This time, however, only some files #required are actually loading - which indicates that the Forth words corresponding to the file name are already in the dictionary.

Could it be that you flashed the file STM8S001J3RS485-forth.ihx (which already contains all the STM8EF-MODBUS Forth code) and not the "empty" file STM8S001J3RS485.ihx?

Hint: it should be possible to do a fresh start by typing RESET - even if the ihx file ending in "-forth" was used.

szymonk159 commented 2 years ago

Thanks for answer. I fixed this problem by flashing STM8S001J3RS485.ihx file as you recommended. Previously I flashed STM8S001J3.ihx. Now uploading files stops during #require :NVM but console on PC5 started working. Probably it is a problem with POSTPONE word. issue3

TG9541 commented 2 years ago

That's an interesting error message :-)

I think that you're using the lib folder from a recent release of STM8 eForth which now uses POSTPONE instead of [COMPILE] and COMPILE in the word :NVM.

The library word :NVM packaged in the zip file in the releases folder uses [COMPILE] like the Forth image packaged there.

Please try the following:

While you're at it please check if target in your working directory is a symlink to out/STM8S001J3RS485/target (e.g. ln -s target out/STM8S001J3RS485/target )

szymonk159 commented 2 years ago

Thank you for answer. My working directory is stm8ef-modbus file. I copied lib,out and target files from stm8ef-bin folder. So the problem is that my Forth uploaded on board is 2.2.24 and I am using lib out and target files based on 2.2.27 Forth version ? (https://github.com/TG9541/stm8ef-modbus/issues/36 I flashed this on my board) How can I find lib from 2.2.24 version or how can I upload STM8S001J3RS485 working with 2.2.27 Forth image because newest release binary does not include STM8S001J3RS485 folder. Or maybe should I flash STM8S001J3.ihx from the newest binary release?

TG9541 commented 2 years ago

Please flash the file STM8S001J3RS485.ihx from the out folder in the release 0.20 archive stm8ef-bin.zip below. The folders mcu and lib there are the ones to use with the binary.

image

szymonk159 commented 2 years ago

Thank you. Now it is working. Do you have any tutorials or advices how can I implement reading temperature in this protocol? I found some comments in board.fs file about using I2C and reading inputs but there is no ready implementation ?

TG9541 commented 2 years ago

There is indeed an interrupt-driver for the STM8 I2C peripheral: mcu/I2CMA. Check out the I2C master examples code.

szymonk159 commented 2 years ago

Hi. And what about 1 wire protocol because this sensors have small sizes and maybe it will be easier than i2c? Can I find some informations about it and implementation in Forth? I want to read temperature using modbus and openhab. Thank you in advance.

TG9541 commented 2 years ago

Sure, that has been done before - there is a simple stand-alone application and one that uses the 1-Wire solution for CamelForth by Brad J. Rodriguez. The second one assumes a more recent version of the STM8 eForth kernel (but, I believe, it can be made work with the one you're using now).

szymonk159 commented 2 years ago

Okay thank you. So now I have working modbus rtu protocol with slave ID 1 (my stm8s103 board). To add temperature sensor (DS18B20) with 1 wire connection I should just upload this 1Wire.md code on my board and then it will be transfering data with my board all time or I should initialize it somehow in modbus protocol like IN@INIT in C0135 project.

TG9541 commented 2 years ago

I would start gaining some experience with the 1-Wire "simple stand-alone application" (1w.readrom is what you need).

In the next step I'd extend the minimal FC04 "Read Register" by reading the temperature sensor directly instead of accessing a register in line 34 with 2* inputs + @.

szymonk159 commented 2 years ago

I am new to Forth but hopefully I can handle it. Thank you for help.

TG9541 commented 2 years ago

I'll lend you a hand. This is for the first step, reading the ROM of a 1-Wire sensor with the command $33 "Read ROM". This should work for getting a reaction from a single 1-Wire sensor - for more than one sensor you'll need the addresses of each of them, or you need to use the discovery method implemented in Brad's code.

Hint: if you need to use a different port pin than PB4 you may need to change the \res export line and the gpio... definitions.

\ STM8 eForth "1-Wire" communication primitives

\res MCU: STM8S103
\res export PB_DDR PB_IDR
#require ]B!
#require ]B?
#require ]CB
#require ]BC
#require :NVM
#require ALIAS

: gpio.d ( -- a c ) PB_DDR 4 ;  \ literals for DDR GPIO
: gpio.i ( -- a c ) PB_IDR 4 ;  \ literals IDR GPIO

:NVM ( -- )    \ GPIO low / dominant
  [ 1 gpio.d ]B!
;RAM ALIAS 1w.l

:NVM ( -- )    \ GPIO floating / input
  [ 0 gpio.d ]B!
;RAM ALIAS 1w.f

:NVM ( n -- )  \ wait n x 2us @16MHz
  FOR [ $9d9d , $9d9d , $9d9d , $9d C, ] NEXT
;RAM ALIAS x2us

NVM
  : 1w.res ( -- f ) \ 1-wire reset with presence check
    1w.l 240 x2us  1w.f 30 x2us  [ gpio.i ]B? NOT 210 x2us
  ;

  : 1w.t ( c -- c ) \ 1-wire transfer c -> c
    7 FOR
      1w.l  1 x2us
      [ $6401 ,       \   SRL (1,X)
        $8C  C,       \   CCF
        gpio.d ]CB  3 x2us  [ gpio.i ]BC
      [ $76  C, ]     \   RRC (X)
      12 x2us
      1w.f  1 x2us
    NEXT EXG
  ;

  VARIABLE RomID 14 ALLOT

  : 1w.readrom
    1w.res
    $33 1w.t drop
    7 FOR
      $FF 1w.t
      [ RomID 7 + ] LITERAL I - C!
    NEXT
  ;
RAM

For testing the sensor run this from the console:

RomID 16 ERASE

: test
    1w.readrom
    RomID 10 DUMP
;
szymonk159 commented 2 years ago

Thank you. I will test it as soon as my temperature sensor arrives.

szymonk159 commented 2 years ago

I connected sensor to PB4 ,wrote RomID ERASE and test definition and console is showing this: 263151968_359410332622985_3845728905114885644_n

TG9541 commented 2 years ago

Great, you're getting the ROM address of the sensor - I've been experimenting a bit with the commands $CC (skip), $44 (convert) and $BE (read scratchpad) - I'll have to try some more tomorrow.

szymonk159 commented 2 years ago

Okay, thank you. I hope this commands will be working and will allow to read temperature. I will be grateful if you will share how to implement it with me. Also I have a question about C0135 board. Today it arrived to me and I want to use DS18B20 sensor also. Is it possible ?

TG9541 commented 2 years ago

Sure, using the inputs of the C0135 to connect DS18B20 sensors is possible (depending on how you see it the unprotected inputs of the board are a feature or a hazard. In this case it's a feature).

You can even use one of the inputs as a sensor VCC output (3.3V, up to 5mA - that's enough for many sensors if temperature conversion is done one at a time).

I'm still figuring out the easiest/safest way to do a conversion.

I assume that you know the address of your sensor.

Questions:

TG9541 commented 2 years ago

Here is a simple implementation for reading temperature values from one or multiple sensors.

https://gist.github.com/TG9541/fe63109242bf18a37853691197c2dd0b#file-1w-ds18b20-fs

Hint: for working with a single sensor you don't need to know the ROM-ID. For more than one sensor you need to know the ROM-IDs, as shown in the example part:

1w.readrom    \ read ROM of single sensor to RomID
RomID 8 DUMP  \ show ROM Id, should start with $28

\ example for fixed/known sensor ID
CREATE MYSENSOR $28 C, $88 C, $16 C, $76 C, $E0 C, $01 C, $3C C, $0A C,

: testid ( a_id -- ) \ read the scratchpad from a sensor with ROM-ID stored at a_id
  DUP 1w.match 1w.conv
      1w.match 1w.scratch
  scratch 9 DUMP
;

RomID testid   \ read the full scratchpad

: testsingle ( -- n )  \ if just one sensor is connected the ROM-ID needs not be known
  1w.skip 1w.conv
  1w.skip 1w.sens
;

testsingle .   \ conversion takes 750ms, so better repeat this
szymonk159 commented 2 years ago

I dont have my sensor ID but I think this adress is proper because it starts with 28. I need one sensor for now. I implemented this and results are on this screen. Is this scratchpad data proper? Thank you in advance. 262896772_2083682525118330_1997915833889153422_n

TG9541 commented 2 years ago

This looks OK, assuming that you to tested the sensor by heating it briefly.

Your ROM-ID is $28 $A $F7 $8A $D $0 $0 $F4, so it can be defined like this:

CREATE MYSENSOR $28 C, $A C, $F7 C, $8A C, $D C, $0 C, $0 C, $F4 C,

If you use a single sensor 1w.skip can be used instead of 1w.match. This means that for starting the conversion and for reading the scratchpad no ROM-ID will be needed.

The scratchpad data looks normal - Byte0 and Byte1, the ones that can be accessed with 1w.sens, contain the sensor data from the last time you called 1w.conv. Byte4 is $7F which means that your sensor is in 12bit mode (that's the default) and the sensor readout from 1w.sens needs to be divided by 16 (the first temperature is about 22.2 C and the peak temperature is 30.9 C).

szymonk159 commented 2 years ago

Okay, thank you very much. So now to run it by modbus this 2 data bytes (byte0 and 1) need to be stored in holding registers and then I will be able to read temperature using modbus?

TG9541 commented 2 years ago

You'll have to make sure that the sensor readout gets refreshed every so often (depending an the rate of the temperature change).

How fast do you intend to read the "holding register"?

If that happens periodically, e.g. with a rate of 1s, it's possible to do read the sensor in a custom FC03-handler (first use 1w.sens, write the result to a "holding variable", then use 1w.conv to prepare a conversion for the next transfer).

Polling rates below 1s require some more effort. In that case take into account that the sensor needs >750ms for a single conversion. Faster conversion rates require a resolution trade-off.

szymonk159 commented 2 years ago

I think that it is not necessary to be often. I am creating basic program for smart homes based on modbus for my engineer's thesis. I think that reading temperature in room can happens less frequently.

TG9541 commented 2 years ago

In that case a rate of 1s is more than enough - that would be sufficient for sensing that a window has been opened.

If you start from the other side I'd recommend that you look at the FC03 code here.

A word like the following might be what you need:

  : DS18B20.sens ( -- n )
    1w.skip 1w.sens
    1w.skip 1w.convert
  ;

The FC03 handler could then be hacked like this:

  \ FC03 handler
  :NVM  ( -- )
    DS18B20.sens holding !
     [ ( xt xth ) SWAP ] LITERAL 16 ( xt bpu ) mbread
  ;NVM ( xth ) 3 FC>XT !
szymonk159 commented 2 years ago

Okay, I modified MBSERVER by adding this DS18B20.sens word. ( I changed this 1w.convert into 1w.conv because 1w.convert was not recognized). I also changed FC03 handler as you recommended. 264279364_629329064862212_2166945742432944579_n . How can I check in Forth console if FC03 handler is working, because in qModMaster when I was reading register with 0x03 by function code it was showing 00 only.

TG9541 commented 2 years ago

Can you please post your FC03 handler code? Does reading with other FCs work, e.g, FC04?

szymonk159 commented 2 years ago

263802674_928570074711387_5689798407627556259_n This is MBSERVER. I just changed FC03 code. Maybe I should implement it in board.fs? There are some comments about reading temperature by I2C. qModMaster is showing only 00 when I am using funcions 0x03 or 0x04 but when I am writing multiple coils first try is showing time out but then I can write vaules and "values written correctly" appears.

TG9541 commented 2 years ago

Writing and reading should work reliably (as shown here). qmodmaster Also, if an FC03 access happens typing holding ? on the console should show a fresh value.

Please test if other Modbus FCs work. By using one of the minimal demo servers in ./test you'll get a console output.

szymonk159 commented 2 years ago

Functions are working fine. I checked in qModMaster. But where the data from temperature is stored? Can I get access to this holding registers with data from sensor by using qmodmaster? qmodmaster

TG9541 commented 2 years ago

Great to see that the communication works! You're using "Holding Register" start address "2" - we've been writing into the first 16bit cell. I would try reading from the first (I don't remember if that is Start Address "1" or "0").

Hint: by writing a non-zero value to the 2nd cell, e.g., $AA55 holding 2+ ! the question "where does it start" is easier to answer. By increading the value of HOLDINGCELLS (by default this is 2) you can also use more "Holding Registers".

szymonk159 commented 2 years ago

tempmodbus Okay thank you so much, it is working now! I changed start addres to 0 and response from the first slave is a value about 0168 hex so 360 dec and it is correct. So in the future I can add more sensors, find their ID, write reading temperature words for each sensor and save data from each to different holding register addres?

TG9541 commented 2 years ago

Okay thank you so much, it is working now! I changed start addres to 0 and response from the first slave is a value about 0168 hex so 360 dec and it is correct. So in the future I can add more sensors, find their ID, write reading temperature words for each sensor and save data from each to different holding register addres?

Yes, that's correct. You can simply create an array of sensor ROM-IDs and address the sensors with the index of the "Holding Register".

If you need a higher rate of temperature measurements it's also possible to issue 1w.conv in a "background task triggered idle task". That should be relatively simple to do (I can provide some example code) and reading from a "holding register" would always return a fresh value.

szymonk159 commented 2 years ago

Yes, you can show me some example code. Maybe it will be usefull for me. In this week I will download openhab and I will try to read temperature by modbus using openhab.