Velbus protocol and Purebasic (control Velbus with PC)

Hello there,

just starting the project to share information for using Velbus with a PC : Purebasic is a great programmation language and would be easy to translate in other language… 8)

Next posts will give more information or example how to control Velbus modules… :arrow_right:

1) Transmit data on the BUS
Not the better part but more easy to understand :

[code]Procedure.i CheckSum(*BufOut,longueur.i)
somme=0
For t=0 To longueur
Somme=Somme+PeekB(*BufOut+t)
Next t
Somme = (Somme & $FF) ! $FF
Somme = Somme + 1
ProcedureReturn Somme
EndProcedure
Procedure.i SendButton(*B, Amodule.i, Abutton.i, StatusButton.i)
PokeB(*B+0,$0F) ; Start
PokeB(*B+1,$F8) ; Priorité
PokeB(*B+2,Amodule) ; Address module(peek an existing address !)
PokeB(*B+3,$04) ; Data = 4 bytes (no RTR)
PokeB(*B+4,$00) ; Push Button Function (H00)
Select StatusButton
Case 0 ; Just Release
PokeB(*B+5,$00) ; Just pressed
PokeB(*B+6,Abutton) ; Just Release
PokeB(*B+7,$00) ; Long Press
Case 1 ; Just Pressed
PokeB(*B+5,Abutton) ; Just pressed
PokeB(*B+6,$00) ; Just Release (NO)
PokeB(*B+7,$00) ; Long Press (NO)
Default ; Just Pressed
PokeB(*B+5,$00) ; Just pressed
PokeB(*B+6,$00) ; Just Release (NO)
PokeB(*B+7,Abutton) ; Long Press (NO)
EndSelect

PokeB(*B+8,CheckSum(*B,8)	)					; Checksum from h0F to Checksum (not included of course)
PokeB(*B+9,$04)						; ETX (End of Transmission)
Commande$ = Hexa2(*B, 10)
Debug "Send => "+Commande$
ProcedureReturn WriteSerialPortData(#CanalCOM, *B, 10)

EndProcedure
[/code]

Here are new procedures : **SetLed **(could make a PushButton led to blink, etc.) and **SwitchRelay **(put the relay ON or OFF)

Fill free to play with these procedures :wink:
Parser to analyse received Velbus frame is on going

[code]; ******************************************************************************
; Velbus communication *
; Author : D. Roumanet Date : 14/09/2009 *
; e-mail : golfy @ free . fr Ver. : 1.0 (Purbasic : 4.x) *
; -----------------------------------------------------------------------------*

Enumeration
#CanalCOM
#Window_0
#Button_0
#String_0
#Button_1
#Editor_0
#Combo_0
EndEnumeration

Global *Buffer = AllocateMemory(2048)
Global *BufOut = AllocateMemory(1024)

Procedure Open_Window_0()
If OpenWindow(#Window_0, 216, 0, 500, 300, “Velbus via VelbusLink”, #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_TitleBar )
ButtonGadget(#Button_0, 20, 20, 70, 20, “Bouton”,#PB_Button_Toggle)
StringGadget(#String_0, 160, 20, 50, 20, “”)
ButtonGadget(#Button_1, 230, 20, 70, 20, “Relais”,#PB_Button_Toggle)
EditorGadget(#Editor_0, 20, 60, 470, 140)
ComboBoxGadget(#Combo_0, 20, 240, 280, 20)
EndIf
EndProcedure
Procedure.s Hexa2(*B,longueur)
; This procedure transform ASCII string in Hexadecimal string (for human). SPACE car. is separator.
r$ = “”
For t = 0 To longueur-1
; Read the buffer from 0 to lengh
a$ = RSet(Hex(PeekB(*B+t) & $FF),2,“0”)+" " ; Rset use to have ‘$0F’ in place of ‘$F’
r$ = r$ + a$
Next t
r$ = r$ + Chr(13)+Chr(10) ; Add CR+LF characters
ProcedureReturn r$
EndProcedure
Procedure LecturePort()
; ******* NOT FINAL PROCEDURE : JUST HELP TO READ BUS AND SEE MESSAGES ********
Resultat = AvailableSerialPortInput(#CanalCOM)
If Resultat
longueur = ReadSerialPortData(#CanalCOM, *Buffer, Resultat)
Commande$ = Hexa2(*Buffer, longueur)
;CommandParser(Commande$)
Debug "Rcv => "+Commande$
Debug “________________”
SetGadgetText(#Editor_0, GetGadgetText(#Editor_0)+Commande$)
EndIf
EndProcedure
Procedure.i CheckSum(*B,longueur.i)
somme=0 ; Initialize Counter
For t=0 To longueur ; Loop from 0 to checksum byte -1
Somme=Somme+PeekB(*B+t) ; Adding each byte from the packet
Next t
Somme = (Somme & $FF) ! $FF ; As PureBasic as signed integer, need to remove higher value
Somme = Somme + 1 ; (AND operation) and inverse with XOR (!), them add ‘1’
ProcedureReturn Somme ; Return the checksum value
EndProcedure
Procedure.i SwitchRelay(*B, Amodule.i, Arelay.i, StatusRelay.i)
PokeB(*B+0,$0F) ; Start
PokeB(*B+1,$F8) ; Priority
PokeB(*B+2,Amodule) ; Address module(peek an existing address !)
PokeB(*B+3,$02) ; Data = 2 bytes (no RTR)
Select StatusRelay
Case 0 ; Relay OFF
PokeB(*B+4,$02) ; OFF
Default ; Relay ON
PokeB(*B+4,$01) ; ON
EndSelect
PokeB(*B+5,Arelay) ; Relay Number (1,2,4,8 else it’s binary : 3 = 1+2 ?)

PokeB(*B+6,CheckSum(*B,5) ) ; Checksum from h0F to Checksum (not included of course)
PokeB(*B+7,$04) ; ETX (End of Transmission)
Commande$ = Hexa2(*B, 8)
Debug "Send => "+Commande$
ProcedureReturn WriteSerialPortData(#CanalCOM, *B, 8)
EndProcedure
Procedure.i SetLed(*B, Amodule.i, Aled.i, StatusLed.i)
; OFF=F5, ON=F6, BlinkSlow=F7, BlinkFast=F8, BlinkVeryFast=F9
;
PokeB(*B+0,$0F) ; Start
PokeB(*B+1,$FB) ; Priority
PokeB(*B+2,Amodule) ; Address module(peek an existing address !)
PokeB(*B+3,$02) ; Data = 2 bytes (no RTR)
Select StatusLed
Case 0 ; Clear
PokeB(*B+4,$F5) ;
Case 1 ; Always ON
PokeB(*B+4,$F6) ;
Case 2 ; Blink Slowly
PokeB(*B+4,$F7) ;
Case 3 ; Blink Fast
PokeB(*B+4,$F8) ;
Default ; Blink Very Fast
PokeB(*B+4,$F9) ;
EndSelect
PokeB(*B+5,Aled) ; LED number

PokeB(*B+6,CheckSum(*B,5) ) ; Checksum from h0F to Checksum (not included of course)
PokeB(*B+7,$04) ; ETX (End of Transmission)
Commande$ = Hexa2(*B, 8)
Debug "Send => "+Commande$
ProcedureReturn WriteSerialPortData(#CanalCOM, *B, 8)
EndProcedure

If OpenSerialPort(#CanalCOM, “COM9”, 9600, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)
Open_Window_0()
If IsWindow(#WIndow_0) = 0
CloseSerialPort(#CanalCOM)
End
EndIf
Repeat
EventID = WaitWindowEvent(20)
LecturePort()

If EventID = #PB_Event_Gadget
  Select EventGadget()
    Case #Button_0
       Debug "Hello_____________________"
       SendButton(*BufOut, 1,2,1)
       SetLed(*BufOut, 1, 3, 2)
       SetLed(*BufOut, 2, 4, 3)
    Case #Button_1
        If GetGadgetState(#Button_1) = 1
           Debug "Relay OFF______________________"
          SwitchRelay(*BufOut, 2,2,0)
          ;SetGadgetState(#Button_1, 0)       
           Else
           Debug "Relay ON_______________________"
          SwitchRelay(*BufOut, 2,2,1)       
          ;SetGadgetState(#Button_1, 1)       

EndIf
EndSelect
EndIf
Until EventID = #PB_Event_CloseWindow
CloseSerialPort(#CanalCOM)
Else
MessageRequester(“Erreur”,“Le port série n’a pas été initialisé”,0)
EndIf
[/code]

Today, I’ve work on reading and pre-parsing Velbus messages :

0FF8010400020000F204 0FFB0102F640BD04 0FFB0102F602FB04 0FFB0208FB020002800000006D04 0FF8020400020000F104 0FF8010400000002F204 0FF8010400000200F204

To do that 8) , I’ve tried to return back in my memory :unamused: , far far far away when writing… recursive’s procedure ! :open_mouth:
The procedure is just below, to avoid neuronal crash for you :laughing:
Now, I’ve to sleep for few day becaus my head is fully ‘out of order’ :frowning: :wink: :stuck_out_tongue:

[code]Procedure.s Hexa2(*B,longueur)
; This procedure transform ASCII string in Hexadecimal string (for human). SPACE car. is separator.

If longueur > 5																				; at least, a 0 byte message should be readable
	taille = PeekB(*B+3)+5															; this procedure would be enhanced with a $0F start test...
	Debug "Len = "+Str(taille)
	If PeekB(*B+taille) = $04														; EOF should be $04 else "bad frame"
		msg$ = ""
		For t = 0 To taille																; Read byte for the first frame
			msg$ = msg$ + RSet(Hex(PeekB(*B+t) & $FF),2,"0")
		Next t
	Else
		Debug Hex(PeekB(*B+taille))+" <> $04"
	EndIf
	
	; first message has been read, message is 1st frame + other message  ;-)
	ProcedureReturn  msg$ + Chr(13)+Chr(10)+Hexa2(*B+taille+1, longueur-(taille+1))
Else																									; less than 5 bytes left ? frame isn't right
	ProcedureReturn msg$																
EndIf

EndProcedure
[/code]

PS: it would be possible that my procedure isn’t fully right

If i’m not affected by the neuronal crash yet, then that’s a very dangerous way of handling your input; Suppose you have a lot of incoming data, your procedure could keep repeatedly calling itself until the stack overflows. I would try putting your parser routine inside a loop without recursion.

en.wikipedia.org/wiki/Stack_overflow

[quote=“VEL448”]

If i’m not affected by the neuronal crash yet, then that’s a very dangerous way of handling your input; Suppose you have a lot of incoming data, your procedure could keep repeatedly calling itself until the stack overflows. I would try putting your parser routine inside a loop without recursion.

en.wikipedia.org/wiki/Stack_overflow[/quote]

Wow is it possible that Velbus messages are so many ???
Even if my COM buffer contains 1024 bytes → Velbus length message (average)= 15 bytes ===> 68 messages
As I’m beginner, I don’t know what is the limit but this could overflows the stack :open_mouth: :question: :question: :question:

The stack is large enough for your example, but your code becomes susceptible to errors, which is considered bad practice. But that’s just my own personal opinion as a programmer, not the law :slight_smile:

Anyways, thanks for sharing all your code for other users to benefit from!

[quote=“VEL448”]The stack is large enough for your example, but your code becomes susceptible to errors, which is considered bad practice. But that’s just my own personal opinion as a programmer, not the law :slight_smile:

Anyways, thanks for sharing all your code for other users to benefit from![/quote]

Thanks for your advice, I’ll take care about… domotic application needs to be stable and reliable !
Dev is different from the past : in 1995, every bobdy was solving everything with recursivity and Programation Oriented Object… :unamused:

Today, I’m trying to use my PC to set the RTC on the 4PB module :

Len = 9
Send RTC => 0F FB 01 04 D8 06 17 1E DE 04
Lowpriority to module 1 message composed by 4 datas : D8 is Set RTC, 06 for Sunday, 23:30 (17 1E), checksum and end

But nothing has happens :frowning:
No so sad because VelbusLink 5 isn’t able to do better (with packet creator)… :laughing:

However, my module still at 0:00 and I need help :unamused:

Set the address byte to 00. The command you are using was designed as a broadcast command, which can not be sent to one specific module.

I see you posted this on sunday, sorry for the late response

Thanks, it’s better :smiley:

Set “Real Time Clock” procedure is following :

Procedure.i SetRTC(*B, Amodule.i, DayNum.i, H.i, M.i)
	; D8 = Set Real Time Clock
	; DayNum = 0 (Munday/Lundi) to 6 'Sunday/Dimanche)
	; H = 0 to 23
	; M = 0 to 59
	;
	PokeB(*B+0,$0F)							; Start
	PokeB(*B+1,$FB)							; Priority (Low)
	PokeB(*B+2,Amodule)					; Address module(peek an existing address !)
	PokeB(*B+3,$04)							; Data = 4 bytes (no RTR)
	PokeB(*B+4,$D8)							; SetClock fonction
	PokeB(*B+5,DayNum%7)				; Day
	PokeB(*B+6,H%24)						; Hour
	PokeB(*B+7,M%60)						; Minutes
		
	PokeB(*B+8,CheckSum(*B,PeekB(*B+3)+3)	)	; Checksum from h0F to Checksum (not included of course)
	PokeB(*B+9,$04)							; ETX (End of Transmission)
	Commande$ = Hexa2(*B, PeekB(*B+3)+6)
	Debug "Send RTC => "+Commande$
	ProcedureReturn WriteSerialPortData(#CanalCOM, *B, PeekB(*B+3)+6)
EndProcedure 

Sorry about long silence but I’m starting 2010 with Win7 replacement : Ubuntu 9.10 is my new OS, so lot of things are changing. :blush:

I still hope developping with Purebasic (purebasic works on Windows OS and Linux OS AND 32 or 64 bits natives applications) : as I’m on Intel 64 bits quadcore, my new live is becoming a little complex. :unamused:

Jeroends seems to have a great project too 8)

argh
My Ubuntu is able to see something but I’m unable to use it !!!

david@Nautilus:/dev/serial/by-id$ ls usb-Velleman_Projects_VMB1USB_Velbus_USB_interface-if00

Purebasic manual speaks about ‘/dev/ttyS0’ but nothing similar in /dev/ directory !!!

However, a good news : the program from Windows version of Purebasic could be compiled under linux : THANKS PUREBASIC !

[size=200]THANKS jeroends !!![/size]

just searching on it post and fall on “ttyACM0” word… change the OpenSeriaPort of the windows version with the following :

If OpenSerialPort(#CanalCOM, "/dev/ttyACM0", 9600, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)

[size=150]IT WORKS ![/size]

Now, I’m ready to continue my dev with Purebasic : the same program source could run on Windows or Linux 8)

Here are reading message procedures, through Ethernet connexion

[code]Procedure LectureNet()
; Read Network buffer and if data are present, convert them in hexa string (human readable)
Resultat = ReceiveNetworkData(ConnectionID, *Buffer, 2048)
If Resultat
Debug "NET BUFFER = "+Str(resultat)
Commande$ = Hexa2(*Buffer, Resultat)
position.i = ListIndex(BusCommand())
;If position < 0 ; first time, it could be -1 (no list)
; position = 0
;EndIf
compte = AnalyseMessage(Commande$)
SelectElement(BusCommand(),position.i+1)
Repeat
Select Mid(BusCommand()\BusCmd, 9, 2)
Case “D9”,“DA”
comment$=“Error”
Case “E5”, “E6”
comment$ = "Temp "
Case “00”
comment$ = "Push "
Case “FB”, “01”, “02”, “03”
comment$ = “Relay”
Case “F4”, “F5”, “F6”, “F7”, “F8”, “F9”
comment$ = "LED "
Case “0F”
comment$ = “Dimme”
EndSelect

		R = AddGadgetItem(#Editor_0, -0, FormatDate("%dd/%mm/%yy %hh:%ii:%ss",Date())+" -> "+comment$+" "+BusCommand()\BusCmd)
		a = NextElement(BusCommand())
	Until a = 0
EndIf

EndProcedure[/code]
NextElement() add a new element in a linked list. That way, there are no limitation to number of message received (only memory).
I can read the linked list at cyclic time to extract informations.
Structure for BusCommand variables is :

Structure Cmd BusTime.i ; Stamp Velbus command reception (format would be date().) BusCmd.s ; The command line in STRING format (human readable) EndStructure

I use this procedure to create real message and avoid partial message. Probably not the best code, but seems to works. I’m not an Object programming guy so my first codes were

10 PRINT "What's your name ?" 20 INPUT A$ 30 PRINT "Hello "+A$

So, be nice with me :wink:

Procedure.i AnalyseMessage(bus.s) ; Format de trame : ; OF FB ** RL xx xx xx CK 04 - 0F FB ** .... cnt.i = 0 LastElement(BusCommand()) Repeat stx = FindString(bus.s, "0F",1) lng = Val("$"+Mid(bus.s, stx+7,1)) etx = stx+(4+lng+1)*2 full= (4+lng+2)*2 If Mid(bus.s,etx,2)="04" c.s = Mid(bus.s,stx,full) AddElement(BusCommand()) BusCommand()\BusTime = Date() BusCommand()\BusCmd = c.s cnt+1 bus.s = RemoveString(bus.s, c.s) EndIf Debug Str(FindString(bus.s, "0F", 1))+" =? "+Str(Len(bus.s)) Until stx < 1 Or Right(bus.s, 2) = "0F" Or Len(bus.s) < 14 Debug Str(cnt)+" -----------------------" ProcedureReturn cnt.i EndProcedure

I make de choice of converting Velbus byte’s messages in hexadecimal string because it’s more easy to read for human and because many commands (in Basic or C programs) use NULL character to end… then manipulations are more easy on hexadecimal string :slight_smile:

Procedure.s Hexa2(*B,longueur.i) ; This procedure transform ASCII string in Hexadecimal string (for human and to avoid chr(0) in string). resultat.s = "" For t = 0 To longueur.i ; Read the buffer from 0 to lengh a$ = RSet(Hex(PeekB(*B+t) & $FF),2,"0") ; Rset use to convert '$F' in '$0F' (2 characters) resultat.s = resultat.s + a$ Next t ProcedureReturn resultat.s EndProcedure

The project is in progress : see screenshot !
data.imagup.com/10/1138720637.9_mainjpg

What does it means ? T&o & T&a can…

  1. poll temperature sensor and graph them (while writing in a CSV file)
  2. start actions at a defined time of the day (dim 0-100% in 20mn for get-up, open blind just 2 seconds, etc.)
  3. monitor the Velbus traffic (recording messages and showing bandwidth)
  4. start (or restart in case of error) velbus-server from Danssaert.
  5. Log error and send email (settings hard-coded :frowning: )

Other functions are coming (scan all modules to avoid manual creation of files, synchronize date on modules to avoid a battery, etc.)
I’ve to create a config file because for now, settings are hard-coded (server IP address, port, path, etc.) but I’ll share an EXE file soon. Source code is too big to big shared in the forum (directly) but I’ll put a link to a ZIP file) :slight_smile:


; ************************************************************************************
; T&o & T&a                                    Velbus communication                  *
; Author : D. Roumanet                         Date : 31/10/2011                     *
; e-mail : golfy @ free . fr                   Ver. : Purebasic : 4.60)              *
; ************************************************************************************
; v0.9 -------------------------------------------------------------------- 16/12/2011
;      (+) Débit (Kbps)
;      (+) Message d'erreur (VelbusErrorAlert)
;      (+) Barre d'état avec Comptage des messages Velbus (procédure Lecture)
;      (*) Transmission de messages Velbus unifiée
;      (*) mise en procédure des graphes
;      (*) amélioration effacement 'vieux messages' (maintenant, sur délai expir.)
;      (.) Correction relance server (uppercase for server name)

; v0.8 -------------------------------------------------------------------- 14/12/2011
;      (*) Modification timer for temperature graph (AddWindowTimer)           
;      (+) Ajout DroopyLib
;      (+) Ajout SendMail
;      (.) Correction réveil + graph (plot hors limite)
;      (+) Procédure RestartVelbusServer (avec condition sur nom PC)
;      (+) Utilisation d'XInclude pour séparation des fonctions
;      (+) Lecture fichier description capteur
; v0.7 -------------------------------------------------------------------- 25/11/2011
;      (+) Added temperature file                                              
;      (+) Added watchguard on Network connection and buffer                   
; v0.5 -------------------------------------------------------------------- 13/11/2011
; 13/11/2011 : Create the new part, TEA (Transmission & Action)                
; 						 Read event file for "alarm"                                     
; v1.2 -------------------------------------------------------------------- 31/10/2011
;      Added TCP/IP stack to use with danssaertd server console                
;	     command : velbus-server COM4 8080													        		  
; v1.1 -------------------------------------------------------------------- 22/10/2011
;      Added GetTempSensor and work on linkedList for Velbus message	         
;	     Messages are stamped to be time deleted							                 	  
; v1.0 -------------------------------------------------------------------- 14/09/2010
;      Initial version write with demo box from Velbus						        		 
; ------------------------------------------------------------------------------------

Looks very promising!

You can have a look on temparature web page of my project :
golfy.free.fr/Velbus/index.html

it’s version 1.05 (versionning cycle for version “1.xx” where xx are evolution AND correction… “2.yz” would be y for evolution and z for correction)
V1 is a kind of beta version : no blocking problem on Velbus but developpement are subject to caution about what it shoud do… and what it does

If someone want to test it, send me your email

[quote=“Golfy”]You can have a look on temparature web page of my project :
roumanet.fr.cr

it’s version 1.05 (versionning cycle for version “1.xx” where xx are evolution AND correction… “2.yz” would be y for evolution and z for correction)
V1 is a king of beta version : no blocking problem on Velbus but developpement are subject to caution about what it shoud do… and what it does

If someone want to test it, send me your email[/quote]

I would love to test it.

New version : 1.06
(+) Added Event Editor (only dimmer for now)

Download project at golfy.free.fr/Velbus/Velbus.zip

  1. Launch a Velbus-server
  2. edit teo-tea.ini for parameter
  3. launch Tea2.exe
  4. Click button [Scan] and close Tea2 (it will ask you if you want to save, click “Yes”)
  5. launch Tea2 again

For event : Heure is from 0:00 to 23:59, Jours is 1=Lundi, 2=Mardi… (you can have a open days event with 12345), Adresse is hexadecimal, action is only “Dimmer” for now…
Don’t forget to check event (uncheck is useful when an event should be suspend for few time)

Button [Bureau], left and right from graph aren’t active and avoid writing a bad address in field near temperature graph else program hangs… but nothing bad… :laughing:

Oh, by the way : Tea generate some files (CSV from temp sensors, PNG picture of the day of sensors and an index.html). Feel free to use a webserver :slight_smile:
golfy.free.fr/Velbus/index.html