Autor Thema: Raspberry Pi + Easy  (Gelesen 98683 mal)

Offline Peter_Pig

  • Jr. Member
  • **
  • Beiträge: 48
Re:Raspberry Pi + Easy
« Antwort #15 am: Juni 22, 2013, 20:55:55 Nachmittag »
Versuchsaufbau:

Easy822-DC-TC
Easy800 USB-CAB
Raspberry Pi mit Python und PySerial Modul



# raspi_com is a script providing serial communication between Raspberry Pi and Easy800 devices via USB-CAB
#
# raspi_com needs pySerial to be previously installed -> http://pyserial.sourceforge.net/pyserial.html
#
# Since this code is Python, it should be portable to other plattforms, but has just been written
# and tested on Raspberry Pi's ARM architecture under Raspbian (a subset of Debian Wheezy)
#
# (c) 2013 Peter Sommer ---------- Dilettanten Ole! ------------ <peter at 3d-check dot com> ---------------
#
# -- This code is free software subject to the GNU General Public License <http://www.gnu.org/licenses/>----
#
# WARNING :
#
# The PLC or it's program MUST restrain or reject received data whenever they don't match process-safe values
#
# The PLC or it's program MUST take care of the process security, being allways and anytime able to deny or
# stop the execution of unsafe actions and undo unsafe process-states triggered by this software
#
# USE ON YOUR OWN RISK
#-----------------------------------------------------------------------------------------------------------

import sys
import struct
import serial
import subprocess
import time

# ----------------------------------------- driver ----------------------------------------------------------
# The cp210x driver is included in recent Linux distributions and just needs --------------------------------
# our USB-Serial bridge's vendor and product ID notifyed to the cp210x driver -------------------------------
# Thanks and free beer plenty for Jon at http://www.ha19.no/usb/ --------------------------------------------

subprocess.Popen('echo 188a 3001 > /sys/bus/usb-serial/drivers/cp210x/new_id',shell=True)

# --------------------------------------------------- ttyUSB port usage -------------------------------------
# -- pySerial encapsulates the access for the serial port -- http://pyserial.sourceforge.net/pyserial.html --
# -----------------------------------------------------------------------------------------------------------

   
def open_port( _name='/dev/ttyUSB0' , _baudrate=9600, _timeout=1 ):

    global o_serial
    global urb

    try:

        o_serial = serial.Serial(_name, baudrate = _baudrate, timeout = _timeout) # create and open serial port
       
        test_mesg = (69,7,0,0,1,0,80,119)               # ---- test the port
        test_mesg = struct.pack('<5BHBB', *test_mesg)
        req=o_serial.write(test_mesg)
        res=o_serial.read(8)

        i_mesg = struct.unpack('<c7B',res)

        in_crc = i_mesg[6:]                             # ---- validate PLC response by CRC16

        in_payload = i_mesg[1:6]

        c_data = struct.pack('<5B',*in_payload)
       
        chk_crc = getCRC(c_data)
       
        if chk_crc == in_crc:
           
            return 0                                    # ---- success: communication ok ---------------- >>>

        else:

            return 1                                    # ---- communication is messy (wrong settings?)
   
    except:

        return 2                                        # ---- no succes creating port
       
def close_port():


    try:
       
        o_serial.close()
        return 0                                        # ---- success
   
    except:

        return 3                                        # ---- no such port
   
       
def write_port(outData):

    try:

        if o_serial.inWaiting() > 0:
           
            urb.append(o_serial.read(o_serial.inWaiting())) # --- flush unrequested data to urb
           
        o_serial.write(outData)

        return listen_port()                                # --- go wait for data

    except:

        return 4                                            # --- no port?

urb = [] # ---- buffer for unrequested data
   
def listen_port():

    try:

        cnt = 0
       
        while cnt < 50 :

            data_len=o_serial.inWaiting()
           
            if data_len > 0:                   
               
                in_header = o_serial.read(1)    # --- get header

                # -------------------------------------- REQUESTED DEVICE TO HOST -------------

                if in_header == 'e':        # --- header from device = response on host request

                    pl_len = o_serial.inWaiting()   
                    in_data = o_serial.read(pl_len)                 
                   
                    in_data = struct.unpack('<'+str(pl_len)+'B',in_data)
                   
                    in_data = list(int(val) for val in in_data)
                   
                    msg_len = in_data[0]            # ---- payload length announced by device
                   
                    if len(in_data)-1 == msg_len:   # ---- if true, guess transfer is completed

                        msg_crc_1 = in_data.pop()   # ---- check data integrity by CRC16
                        msg_crc_2 = in_data.pop()                   

                        c_payload = struct.pack('<'+str(len(in_data))+'B', *in_data)
                        crc = getCRC(c_payload)
                       
                        if msg_crc_1 == crc[1] and msg_crc_2 == crc[0]:   #  data is good!
                           
                            return in_data                      # --- return validated data -->>>
                           
                       
                        else:

                            return 5                            # --- data was a mess

                    else:                                       
                            return 6                            # --- data wrong length
                       
                           

                # -------------------------------------- UNREQUESTED DEVICE TO HOST -------------
               
                else:

                     urb.append(in_header+o_serial.readline())

                     while o_serial.inWaiting() > 0:
                         
                         urb.append(o_serial.readline())        # flush unrequested data to urb
                     
                     return 7                                   # just had unrequested stuff
                   
            else:

                time.sleep(.1)
                cnt=cnt+1

        return 8                                                # ---- function timed out

    except:

        return 9                                    # ---- port is closed or does not exist

# --------------------------------------------------------------------------------------------------   
# ----      plc_item objects provide easy read and write access                                 ----
# ----      they build the "front end" of raspi_com                                             ----
# --------------------------------------------------------------------------------------------------

# ----      dictionaries used for message encoding/decoding (kept public allow edit during runtime)

d_write= {'M':(4,1,'B'),'MB':(10,1,'B'),'MW':(10,2,'H'),'MD':(10,4,'i')} #(type,indx-mult,width)

d_read = {'QA':(9,0,'H'),'IA':(8,0,'4H'),'I':(0,0,'H'),'Q':(1,0,'H'),'M':(4,1,'B'),
          'MB':(10,1,'32B'), 'MW':(10,2,'16H'),'MD':(10,4,'8i')}         #(type,indx-mult, fmt)

# --------- create plc_item instances using this sintax: -------------------------------------------
#                                                                                               ----
#           object = plc_item(location as int, item as string, index as int)                    ----
#                                                                                               ----
# --------------------------------------------------------------------------------------------------

class plc_item:

    def __init__(self, location=0, item_type='Q', index=0):

        self.error = None
        self.timestamp = 0
        self.is_writeable = False
       
        try:
           
            self.location = int(location)

            if d_read.has_key(item_type):
               
                self.item_type = item_type

            else:

                return   # --- unknown PLC item type
       
            self.index = int(index)-1
       
            self.msg_h = [self.location,d_read[self.item_type][0],self.index*d_read[self.item_type][1]]

            self.is_writeable = d_write.has_key(item_type)

            self.error = 0
           
        except:

            return   # --- error creating plc_item
       
    def set_value(self, value):

        if self.is_writeable == True:

            try:
               
                data = struct.pack(d_write[self.item_type][2],value)
                data = struct.unpack(str(d_write[self.item_type][1])+'B',data)
                o_mesg = [self.location,d_write[self.item_type][0],self.index*d_write[self.item_type][1], data]

                resp = write_object(*o_mesg)

                try:

                    resp.pop(0)
                    err_b_1 = resp.pop(0)
                    err_b_2 = resp.pop(0)

                    if err_b_1+err_b_2 == 0:

                        return 0    # --------- success ----- >

                    else:

                        self.error=(err_b_1,err_b_2,resp.pop(0))
                       
                        return 10  # -------- device reported error to be read from self.error

                except:

                     return resp # ------ internal error code (int)
                   
            except:

                return 11  # wrong data ?

        else:

            return 12 # --- plc_item is not writeable
       
   
    def get_value(self):

        if self.error != None :

            resp = read_object(*self.msg_h)
           
            try:
               
                resp.pop(0)
                err_b_1 = resp.pop(0)
                err_b_2 = resp.pop(0)
               
                if err_b_1+err_b_2 == 0:

                    c_mesg = struct.pack(str(len(resp))+'B',*resp)
                    o_mesg = struct.unpack(d_read[self.item_type][2],c_mesg)
           
                    self.values = o_mesg
                    self.error = ()
                    self.timestamp = time.clock()
                   
                    return self.values  # ----- success (data as tuple)

                else:

                    self.error = resp.pop(0)
                   
                    return 13  #---- device reported error to be read from self.error

            except:

                return resp # ----- internal error code

        else:

            return 14 # ---- this plc_item is not working

   
# ------------------------------------------------ message encoding -------------------------------
# -------------------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------------------

def write_object(B_net_id,B_object,H_index,data): # ----- request write to device memory
   
    try:

        a_data = [int(val) for val in data]
   
        o_msg = [len(a_data)+7, 32, B_net_id, B_object, H_index]+a_data

        c_msg = struct.pack('<4BH'+str(len(a_data))+'B', *o_msg)

    except:

        return 15 # ---- wrong data

    crc = getCRC(c_msg)

    o_msg.insert(0,0x45) # --- header ('E')
    o_msg.append(crc[0]) # --- CRC-16 byte 1
    o_msg.append(crc[1]) # --- CRC-16 byte 0
   
    c_msg = struct.pack('<5BH'+str(2+len(a_data))+'B',*o_msg)

    return write_port(c_msg)    # --- wait for success ------------------------------------------->


def read_object(B_net_id,B_object,H_index):       # ----- request data from device memory

    try:
       
        o_msg = [7, 0, B_net_id, B_object, H_index]
       
        c_msg = struct.pack('<4BH', *o_msg)

    except:

        return 16 # ---- wrong data

    crc = getCRC(c_msg)

    o_msg.insert(0,0x45)  # --- header ('E')
    o_msg.append(crc[0])  # --- CRC-16 byte 1   
    o_msg.append(crc[1])  # --- CRC-16 byte 0

    c_msg = struct.pack('<5BHBB',*o_msg)

    return write_port(c_msg)    # --- wait for data ----------------------------------------------->

# --------------------------------------------------------------------------------------------------
# ------  methods dealing with unrequested device to host messages                              ----
# --------------------------------------------------------------------------------------------------
# ------  flush_data set to False performs a conservative dump

# --------------------------------------------------------- read one item from urb -----------------

def pop_urb(flush_data = True, offset=0):

    global urb

    if len(urb) > offset+1:

        if flush_data:
           
            return urb.pop(offset)        # - returns one line as string and deletes it from the list

        else:

            return urb[offset]            # - just returns one line as string
    else:
       
        return ''

# -------------------------------------------------------------- read all data from urb -------------
   
def dump_urb(flush_data = True):
   
    global urb
   
    ret = urb

    if flush_data:
       
        urb=[]     
   
    return ret                              # - returns urb's data as a list



#---------------------------------------------------- CRC ------------------------------------------------
# Table-based CRC-16 calculation from http://www.digi.com/wiki/developer/index.php/Python_CRC16_Modbus_DF1
# shortened functionality for ease of use in our case, and messy byte juggling added by needs ------------
# Lots of thanks and plenty of gallons free beer go to Greg Cook, for his tool "crc revEng" + advise -----
# http://regregex.bbcmicro.net ---------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------------------


def getCRC( st ):

    crc16_table = (
        0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
        0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
        0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
        0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
        0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
        0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
        0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
        0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
        0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
        0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
        0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
        0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
        0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
        0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
        0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
        0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
        0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
        0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
        0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
        0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
        0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
        0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
        0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
        0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
        0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
        0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
        0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
        0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
        0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
        0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
        0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
        0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 )


    crc = 0x0000
   
    for ch in st:
       
        crc = (crc >> 8) ^ crc16_table[(crc ^ ord(ch)) & 0xFF]
       
    crc = struct.pack('<H',crc)
    crc = struct.unpack('<BB',crc)
         
    return crc
 
# ------------------------------------------------------------------------------------------------ END



Zusätzlich braucht es das PySerial Modul.

So wird es am einfachsten benutzt :

Man kreiert beliebige Instanzen von plc_item, die dann die Methoden set_value(data) und get_value() zur verfügung stellen.

o_item = plc_item(net_id , type, index)

net_id ist 1 byte Integer
type ist String
index ist 2 byte Integer

Implementiert sind folgende "typen":

M = Merker Bit / Index 1 bis 96 / set_value 1 Bit (0 oder 1) / get_value 1 Bit (0 oder 1)

MB = Merker Byte / Index 1 bis 384 / set_value 1 Byte (0 bis 255) / get_value 32 byte

MW = Merker Wort / Index 1 bis 192 / set_value 1 Word (0 bis 65535) / get_value 16 Word

MD = Merker Doppelwort / Index 1 bis 96 / set_value 1 DWord (+/- 2147483646) / get_value 8 DWord

Q = Ausgänge / kein Index / kein Schreibzugriff / get_value 1 Word

QA = Analogausgang / kein Index / kein Schreibzugriff / get_value 1 Word

IA = Analogeingänge / kein Index / kein Schreibzugriff / get_value 4 Word

I = Digitaleingänge / kein Index / kein Schreibzugriff / get_value 1 Word


Zu mehr war ich nicht in der Lage...  ::)


get_value() gibt bei Erfolg ein Tuple zurück, mit den Werten ab Index.
 
Beim lesen von MB,MW und MD bekommt man immer 32 Byte Daten zurück (ab Index).

Ein kleines Beispiel aus der IDLE Konsole:


>>> open_port()
0
>>> M1 = plc_item(0,'M',1)
>>> M1.set_value(1)
[5,0,0,0]    <-------------------- die Nullen hinter der 5 bedeuten Erfolg
>>> M1.get_value()
(1,)
>>> MD1 = plc_item(0,'MD',1)
>>> MD1.set_value(-12345)
[5,0,0,0]
>>> MD1.get_value()
(-12345,0,0,0,0,0,0,0)
>>>close_port()
0


Übrigens: wenn jemand den Baustein "Serielles Protokoll" (LS) in sein Easy-Projekt einbaut, kann die Easy ungefragt auf die Serielle Schnittstelle schreiben.

Bei plc_item.set_value() und plc_item.get_value() werden diese "ungefragten" Daten, sollten sie Anliegen, erst einmal ausgelesen und in der Liste urb gepuffert und können mit pop_urb() und dump_urb() danach ausgelesen werden.

Möglicher Einsatz : Data-Logging

Vielleicht hilft es jemanden.

Gruß,

Peter
« Letzte Änderung: Juni 30, 2013, 12:01:20 Nachmittag von Peter_Pig »

Offline Peter_Pig

  • Jr. Member
  • **
  • Beiträge: 48
Re:Raspberry Pi + Easy
« Antwort #16 am: Juni 30, 2013, 11:52:07 Vormittag »
 :-\ Ich bin mir nicht sicher, ob plc_item() richtig funktioniert, wenn eine andere Net-ID angesprochen wird als 0 (Lokal).

>>> open_port()
0
>>> M1ID1=plc_item(1,'M',1)
>>> M1ID1.get_value()
(0,) <--- falsch! das ist der Wert von Net-ID 0
>>> M1ID1.set_value(1)
(5,1,0,25) <--- fehlermeldung von Easy

Zwar bekomme ich bei plc_item.set_value() eine Fehlermeldung von Easy zurück, wenn ich einen nicht vorhandenen Teilnehmer ansprechen will, bei plc_item.get_value() bekomme ich aber dann immer die Werte der Net-ID 0 zurück und keine Fehlermeldung.

Kann leider nicht testen wie es mit einem Angeschlossenem Net-Teilnehmer aussehen würde ...

Meine Vermutung war wohl falsch, dass das vierte Byte der Net-ID entspricht.

Gruß,

Peter

Offline arnenrw

  • Newbie
  • *
  • Beiträge: 6
Re:Raspberry Pi + Easy
« Antwort #17 am: Juli 09, 2013, 16:26:46 Nachmittag »
es ist das 3 Byte (davon das Low-Byte)

Offline Peter_Pig

  • Jr. Member
  • **
  • Beiträge: 48
Re:Raspberry Pi + Easy
« Antwort #18 am: Juli 09, 2013, 22:26:03 Nachmittag »
Hi Arne,

danke für die Rückmeldung.

Leider habe ich es nicht ganz kapiert...

Die NET-ID steht im Low-Byte des dritten Byte? Kannst Du mir bitte ein Beispiel geben?

Vielen Dank im Voraus!

Peter

Offline Peter_Pig

  • Jr. Member
  • **
  • Beiträge: 48
Re:Raspberry Pi + Easy
« Antwort #19 am: Dezember 20, 2013, 20:21:22 Nachmittag »
Hi Arne,

danke für dein Tip.

Es hat nur ein halbes Jahr gedauert, bis ich es umgesetzt habe  :(

Jetzt meckert es endlich, wenn man versucht einen nicht vorhandenen Teilnehmer anzusprechen/auszulesen ... ich kann aber nicht ausprobieren ob es wirkilch funtzt wenn der Net-Teilnehmer tatsächlich vorhanden ist.

Sollte aber funktionieren.

Gruß und frohe Weihnachten (auch an die Admins ... oder wurden die wegrationalisiert?!)


Peter

P.S:

Anstatt :


o_msg = [len(a_data)+7, 32, B_net_id, B_object, H_index]+a_data


schreibts sich jetzt:





        o_msg = [len(a_data)+7, 32 + B_net_id, 0, B_object, H_index]+a_data


Jetzt ist ein Byte übrig, der fest mit Null belegt wird ... ::)

Das Ergebniss:

# raspy_com is a script providing serial communication between Raspberry Pi and Easy800 devices via USB-CAB
#
# raspy_com needs pySerial to be previously installed -> http://pyserial.sourceforge.net/pyserial.html
#
# Since this code is Python, it should be portable to other plattforms, but has just been written
# and tested on Raspberry Pi's ARM architecture under Raspbian (a subset of Debian Wheezy)
#
# (c) 2013 Peter Sommer ---------- Dilettanten Ole! ------------ <peter at 3d-check dot com> ---------------
#
# -- This code is free software subject to the GNU General Public License <http://www.gnu.org/licenses/>----
#
# WARNING :
#
# The PLC or it's program MUST restrain or reject received data whenever they don't match process-safe values
#
# The PLC or it's program MUST take care of the process security, being allways and anytime able to deny or
# stop the execution of unsafe actions and undo unsafe process-states triggered by this software
#
# USE ON YOUR OWN RISK
#-----------------------------------------------------------------------------------------------------------

import sys
import struct
import serial
import subprocess
import time

# ----------------------------------------- driver ----------------------------------------------------------
# The cp210x driver is included in recent Linux distributions and just needs --------------------------------
# our USB-Serial bridge's vendor and product ID notifyed to the cp210x driver -------------------------------
# Thanks and free beer plenty for Jon at http://www.ha19.no/usb/ --------------------------------------------

subprocess.Popen('echo 188a 3001 > /sys/bus/usb-serial/drivers/cp210x/new_id',shell=True)

# --------------------------------------------------- ttyUSB port usage -------------------------------------
# -- pySerial encapsulates the access for the serial port -- http://pyserial.sourceforge.net/pyserial.html --
# -----------------------------------------------------------------------------------------------------------

    
def open_port( _name='/dev/ttyUSB0' , _baudrate=9600, _timeout=1 ):

    global o_serial
    global urb

    try:

        o_serial = serial.Serial(_name, baudrate = _baudrate, timeout = _timeout) # create and open serial port
        
        test_mesg = (69,7,0,0,1,0,80,119)               # ---- test the port
        test_mesg = struct.pack('<5BHBB', *test_mesg)
        req=o_serial.write(test_mesg)
        res=o_serial.read(8)

        i_mesg = struct.unpack('<c7B',res)

        in_crc = i_mesg[6:]                             # ---- validate PLC response by CRC16

        in_payload = i_mesg[1:6]

        c_data = struct.pack('<5B',*in_payload)
        
        chk_crc = getCRC(c_data)
        
        if chk_crc == in_crc:
            
            return 0                                    # ---- success: communication ok ---------------- >>>

        else:

            return 1                                    # ---- communication is messy (wrong settings?)
    
    except:

        return 2                                        # ---- no succes creating port (check connections)
        
def close_port():


    try:
        
        o_serial.close()
        return 0                                        # ---- success
    
    except:

        return 3                                        # ---- no such port
    
      
def write_port(outData):

    try:

        if o_serial.inWaiting() > 0:
            
            urb.append(o_serial.read(o_serial.inWaiting())) # --- flush unrequested data to urb
            
        o_serial.write(outData)

        return listen_port()                                # --- go wait for data

    except:

        return 4                                            # --- no port?

urb = [] # ---- buffer for unrequested data
    
def listen_port():

    try:

        cnt = 0
        
        while cnt < 50 :

            data_len=o_serial.inWaiting()
            
            if data_len > 0:                    
                
                in_header = o_serial.read(1)    # --- get header

                # -------------------------------------- REQUESTED DEVICE TO HOST -------------

                if in_header == 'e':        # --- header from device = response on host request

                    pl_len = o_serial.inWaiting()  
                    in_data = o_serial.read(pl_len)                
                    
                    in_data = struct.unpack('<'+str(pl_len)+'B',in_data)
                    
                    in_data = list(int(val) for val in in_data)
                    
                    msg_len = in_data[0]            # ---- payload length announced by device
                    
                    if len(in_data)-1 == msg_len:   # ---- if true, guess transfer is completed

                        msg_crc_1 = in_data.pop()   # ---- check data integrity by CRC16
                        msg_crc_2 = in_data.pop()                  

                        c_payload = struct.pack('<'+str(len(in_data))+'B', *in_data)
                        crc = getCRC(c_payload)
                        
                        if msg_crc_1 == crc[1] and msg_crc_2 == crc[0]:   #  data is good!
                            
                            return in_data                      # --- return validated data -->>>
                            
                        
                        else:

                            return 5                            # --- data was a mess

                    else:                                      
                            return 6                            # --- data wrong length
                        
                            

                # -------------------------------------- UNREQUESTED DEVICE TO HOST -------------
                
                else:

                     urb.append(in_header+o_serial.readline())

                     while o_serial.inWaiting() > 0:
                        
                         urb.append(o_serial.readline())        # flush unrequested data to urb
                    
                     return 7                                   # just had unrequested stuff
                  
            else:

                time.sleep(.1)
                cnt=cnt+1

        return 8                                                # ---- function timed out

    except:

        return 9                                    # ---- port is closed or does not exist

# --------------------------------------------------------------------------------------------------    
# ----      plc_item objects provide easy read and write access                                 ----
# ----      they build the "front end" of raspy_com                                             ----
# --------------------------------------------------------------------------------------------------

# ----      dictionaries used for message encoding/decoding (kept public allow edit during runtime)

d_write= {'M':(4,1,'B'),'MB':(10,1,'B'),'MW':(10,2,'H'),'MD':(10,4,'i')} #(type,indx-mult,width)

d_read = {'QA':(9,0,'H'),'IA':(8,0,'4H'),'I':(0,0,'H'),'Q':(1,0,'H'),'M':(4,1,'B'),
          'MB':(10,1,'32B'), 'MW':(10,2,'16H'),'MD':(10,4,'8i')}         #(type,indx-mult, fmt)

# --------- create plc_item instances using this sintax: -------------------------------------------
#                                                                                               ----
#           object = plc_item(location as int, item as string, index as int)                    ----
#                                                                                               ----
# --------------------------------------------------------------------------------------------------

class plc_item:

    def __init__(self, location=0, item_type='Q', index=0):

        self.error = None
        self.timestamp = 0
        self.is_writeable = False
        
        try:
            
            self.location = int(location)

            if d_read.has_key(item_type):
                
                self.item_type = item_type

            else:

                return   # --- unknown PLC item type
        
            self.index = int(index)-1
        
            self.msg_h = [self.location,d_read[self.item_type][0],self.index*d_read[self.item_type][1]]

            self.is_writeable = d_write.has_key(item_type)

            self.error = 0
            
        except:

            return   # --- error creating plc_item
        
    def set_value(self, value):

        if self.is_writeable == True:

            try:
                
                data = struct.pack(d_write[self.item_type][2],value)
                data = struct.unpack(str(d_write[self.item_type][1])+'B',data)
                o_mesg = [self.location,d_write[self.item_type][0],self.index*d_write[self.item_type][1], data]

                resp = write_object(*o_mesg)

                try:

                    resp.pop(0)
                    err_b_1 = resp.pop(0)
                    err_b_2 = resp.pop(0)

                    if err_b_1+err_b_2 == 0:

                        return 0    # --------- success ----- >

                    else:

                        self.error=(err_b_1,err_b_2,resp.pop(0))
                        
                        return 10  # -------- device reported error to be read from self.error

                except:

                     return resp # ------ internal error code (int)
                    
            except:

                return 11  # wrong data ?

        else:

            return 12 # --- plc_item is not writeable
        
    
    def get_value(self):

        if self.error != None :

            resp = read_object(*self.msg_h)
            
            try:
                
                resp.pop(0)
                err_b_1 = resp.pop(0)
                err_b_2 = resp.pop(0)
                
                if err_b_1+err_b_2 == 0:

                    c_mesg = struct.pack(str(len(resp))+'B',*resp)
                    o_mesg = struct.unpack(d_read[self.item_type][2],c_mesg)
            
                    self.values = o_mesg
                    self.error = ()
                    self.timestamp = time.time()
                    
                    return self.values  # ----- success (data as tuple)

                else:

                    self.error = (err_b_1,err_b_2,resp.pop(0))
                    
                    return 13  #---- device reported error to be read from self.error

            except:

                return resp # ----- internal error code

        else:

            return 14 # ---- this plc_item is not working

    
# ------------------------------------------------ message encoding -------------------------------
# -------------------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------------------

def write_object(B_net_id,B_object,H_index,data): # ----- request write to device memory
    
    try:

        a_data = [int(val) for val in data]
    
        o_msg = [len(a_data)+7, 32 + B_net_id, 0, B_object, H_index]+a_data

        c_msg = struct.pack('<4BH'+str(len(a_data))+'B', *o_msg)

    except:

        return 15 # ---- wrong data

    crc = getCRC(c_msg)

    o_msg.insert(0,0x45) # --- header ('E')
    o_msg.append(crc[0]) # --- CRC-16 byte 1
    o_msg.append(crc[1]) # --- CRC-16 byte 0
    
    c_msg = struct.pack('<5BH'+str(2+len(a_data))+'B',*o_msg)

    return write_port(c_msg)    # --- wait for success ------------------------------------------->


def read_object(B_net_id,B_object,H_index):       # ----- request data from device memory

    try:
        
        o_msg = [7, 0, B_net_id, B_object, H_index]
        
        c_msg = struct.pack('<4BH', *o_msg)

    except:

        return 16 # ---- wrong data

    crc = getCRC(c_msg)

    o_msg.insert(0,0x45)  # --- header ('E')
    o_msg.append(crc[0])  # --- CRC-16 byte 1  
    o_msg.append(crc[1])  # --- CRC-16 byte 0

    c_msg = struct.pack('<5BHBB',*o_msg)

    return write_port(c_msg)    # --- wait for data ----------------------------------------------->

# --------------------------------------------------------------------------------------------------
# ------  methods dealing with unrequested device to host messages                              ----
# --------------------------------------------------------------------------------------------------
# ------  flush_data set to False performs a conservative dump

# --------------------------------------------------------- read one item from urb -----------------

def pop_urb(flush_data = True, offset=0):

    global urb

    if len(urb) > offset+1:

        if flush_data:
            
            return urb.pop(offset)        # - returns one line as string and deletes it from the list

        else:

            return urb[offset]            # - just returns one line as string
    else:
        
        return ''

# -------------------------------------------------------------- read all data from urb -------------
  
def dump_urb(flush_data = True):
    
    global urb
    
    ret = urb

    if flush_data:
        
        urb=[]      
    
    return ret                              # - returns urb's data as a list



#---------------------------------------------------- CRC ------------------------------------------------
# Table-based CRC-16 calculation from http://www.digi.com/wiki/developer/index.php/Python_CRC16_Modbus_DF1
# shortened functionality for ease of use in our case, and messy byte juggling added by needs ------------
# Lots of thanks and plenty of gallons free beer go to Greg Cook, for his tool "crc revEng" + advise -----
# http://regregex.bbcmicro.net ---------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------------------


def getCRC( st ):

    crc16_table = (
        0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
        0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
        0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
        0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
        0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
        0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
        0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
        0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
        0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
        0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
        0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
        0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
        0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
        0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
        0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
        0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
        0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
        0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
        0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
        0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
        0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
        0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
        0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
        0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
        0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
        0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
        0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
        0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
        0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
        0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
        0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
        0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 )


    crc = 0x0000
    
    for ch in st:
        
        crc = (crc >> 8) ^ crc16_table[(crc ^ ord(ch)) & 0xFF]
        
    crc = struct.pack('<H',crc)
    crc = struct.unpack('<BB',crc)
          
    return crc
 
# ------------------------------------------------------------------------------------------------ END
« Letzte Änderung: Dezember 20, 2013, 20:37:09 Nachmittag von Peter_Pig »

Offline HORR

  • Full Member
  • ***
  • Beiträge: 145
Re:Raspberry Pi + Easy
« Antwort #20 am: August 13, 2014, 11:39:02 Vormittag »
gibt es zu diesem Thema nichts neues ?
oder hat einer neue Bastelideen

Offline heicar

  • Newbie
  • *
  • Beiträge: 14
Re:Raspberry Pi + Easy
« Antwort #21 am: August 21, 2014, 13:10:10 Nachmittag »
So, hab jetzt auch endlich einen Rapsi bestellt. Habe aber zur Kommunikation mit der Easy bisher immer das Serielle Kabel benutzt. Sollte doch auch funktionieren mit dem obigen Progrämmsche. Natürlich das Device etc. im Programm anpassen.

Ich bin mal gespannt....

Gruß Heicar

Offline Peter_Pig

  • Jr. Member
  • **
  • Beiträge: 48
Re:Raspberry Pi + Easy
« Antwort #22 am: August 25, 2014, 13:48:04 Nachmittag »
Viel Glück Heicar, und lass uns wissen, wie das mit dem seriellen Kabel klappt.

Gruß,

Peter

Offline heicar

  • Newbie
  • *
  • Beiträge: 14
Re:Raspberry Pi + Easy
« Antwort #23 am: August 30, 2014, 16:14:55 Nachmittag »

blöde Frage noch  - kenne mich mit Python nicht so recht aus.

Wie rufe ich das Script denn nun auf - hast du mal ein kurzes Beispiel ??

Danke dir


----- Nachtrag

Hab jetzt in der Python Console probiert.

Gleich beim open_port() kommt schon als Antwort "2"
Lustiger Weise kann ich aber Merker setzen mit set_value - läuft dann aber in den Timeout (8)

Get_value() läuft in den Timeout (8) - ok wg. Port

Wieso kann ich Merker setzen, wenn doch angeblich der Port nicht richtig initialiesiert ist?

wenn ich ein close_port() ausführe kann ich keine Merker setzen - Port ist also geöffnet -hmm


Ich benutze eine USB auf Seriell Adapter
Bus 001 Device 004: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port


Wie gesagt - Python ist mir komplett neu.... Im Momemt mehr try und ganz viel Error.....
« Letzte Änderung: August 30, 2014, 17:16:24 Nachmittag von heicar »

Offline heicar

  • Newbie
  • *
  • Beiträge: 14
Re:Raspberry Pi + Easy
« Antwort #24 am: August 31, 2014, 11:25:43 Vormittag »
so,
- habe einen anderen Rechner (Desktop - auch linux) genommen - gleiches Ergebnis
- habe eine andere 800er genommen Netid2 - gleiches Ergebnis
- habe Serielles Baustein auf Netid2 eingebaut - keine Auswirkungen

Hmmm, weiß jetzt nicht weiter. Ich kann Merker setzen - aber keine Chance irgendwas von der Easy zu empfangen.

und nu? Wer hat mal einen Denkanstoß für mich??

Danke Heicar


Offline Peter_Pig

  • Jr. Member
  • **
  • Beiträge: 48
Re:Raspberry Pi + Easy
« Antwort #25 am: September 01, 2014, 20:21:27 Nachmittag »
Hi Heicar!

Erstmals danke für deine Bemühungen.

Es tut mir leid, dass eine Reaktion auf deine Fragen so spät kommt...

Bist Du Dir sicher, dass die Merker, die Du mit set_value() setzen willst, auch wirklich auf der Easy gesetzt werden?

Die Funktion write_port() hat nämlich keine Möglichkeit zu prüfen, ob die Easy überhaupt etwas empfangen, geschweige denn "verstanden" oder ausgeführt hat. Dies wird erst durch "listen_port()" bewerkstelligt. Dort wird die Antwort der Easy auf unsere write Anweisung erwartet. Wenn nichts kommt, dann wird 8 (timeout) zurückgegeben.

Das wäre wichtig zu wissen...

Auf jeden Fall ist es wichtig, dass erst einmal open_port() "0" als Rückgabewert bringt. Sonst ist irgendwo der Hund drinn.

Wenn Du open_port() ohne Übergabeparameter verwendest, werden die Default-Werte für _name und _baudrate übernommen ('dev/ttyUSB0' und 9600 baud).

Es kann sein, dass eins von beiden nicht stimmt.

Füge mal in die Funktion open_port() eine Zeile direkt über "return 2" eine Zeile mit  "raise" ein.

Dann bekommst Du eine Fehlermeldung... die hilft uns vielleicht weiter.

Viel Glück!

Gruß,

Peter

P.S.: Ich habe das Skript übrigens inzwischen auch auf Windows (Vista 32 und Windows7 64) Maschinen laufen lassen, mit Erfolg (das ist aber nicht unbedingt Sinn der Sache). Jedoch immer mit dem USB-CAB.






 
« Letzte Änderung: September 01, 2014, 20:32:57 Nachmittag von Peter_Pig »

Offline heicar

  • Newbie
  • *
  • Beiträge: 14
Re:Raspberry Pi + Easy
« Antwort #26 am: September 02, 2014, 17:55:28 Nachmittag »
Hallo

erstmal zu der Fehlermeldung nach dem raise

    i_mesg = struct.unpack('<c7B',res)
struct.error: unpack requires a string argument of length 8


Scheint logisch die Fehlermeldung - es kommt wohl auch gar nichts an.

Ich kann definitiv setzen. Ich habe einen Merker über das Programm gesetzt das welcher sonst nicht vom Programm aus gesetzt wird.
Sowohl auf 1 als auf 0 setzten geht.
Ich habe das Kabel zur SPS auf die 3. Easy umgesetzt. Gleiches Problem ich kann den Merker auf Netid 1 setzen, bekomm aber keine Rückmeldung.

Achso - der Merker den ich setzte schaltet eine Lampe die gleich neben mir ist. Also die visuelle Kontrolle ist ok! :-)

Kann es wirklich am Kabel liegen???? Kann ich senden, aber nichts empfangen ist das möglich?????

Die open Port Parameter habe ich auch andere Varianten getestet - dort ging dann nichts.

Wirklich das Kabel????

Hilfe  ???

Offline Peter_Pig

  • Jr. Member
  • **
  • Beiträge: 48
Re:Raspberry Pi + Easy
« Antwort #27 am: September 02, 2014, 22:38:45 Nachmittag »
 :-\ da weiss ich wirklich nicht weiter ...

Den richtigen Linux-Treiber für dein Kabel hast Du ja scheinbar ...

Anbei die neueste Version als Download. Da gibt es eine Funktion scan_baudrates() die versucht mit Geduld eine Verbindung zur Easy herzustellen.

Probier diese Funktion mal aus, ohne vorher open_port() aufzurufen.

Was kommen da für meldungen auf der Konsole?

Danke für deine Hilfe!

Gruß,

Peter

Offline heicar

  • Newbie
  • *
  • Beiträge: 14
Re:Raspberry Pi + Easy
« Antwort #28 am: September 03, 2014, 17:18:11 Nachmittag »
Hallo Peter

folgendes erscheint

>>> scan_baudrates()
Trying to connect /dev/ttyUSB0 at 9600 baud
Error 8
Trying to connect /dev/ttyUSB0 at 19200 baud
Error 8
Trying to connect /dev/ttyUSB0 at 38400 baud
Error 8
Trying to connect /dev/ttyUSB0 at 57600 baud
Error 8
1022
>>>


Danke für deine Hilfe


Offline Peter_Pig

  • Jr. Member
  • **
  • Beiträge: 48
Re:Raspberry Pi + Easy
« Antwort #29 am: September 03, 2014, 18:33:52 Nachmittag »
Hi Heicar ... die Nuss müssen wir doch knacken können ... :-X

In der Funktion listen_port() ist direkt über return 8 die Zeile  time.sleep(.01) mit # auskommentiert.

Lösche mal das # und mal schauen was passiert.

Kannst auch höhere Zeiten eingeben ... meinetwegen time.sleep(.1)

Mal sehen was passiert.

Danke!

Gruß,

Peter