Skip to content

Instantly share code, notes, and snippets.

@mdijkens
Created April 7, 2018 19:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mdijkens/0f28abc5bb5855871c53b5ff1e6863c3 to your computer and use it in GitHub Desktop.
Save mdijkens/0f28abc5bb5855871c53b5ff1e6863c3 to your computer and use it in GitHub Desktop.
ZKE Tech EBD+ protocol in QB64
' ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
' º º
' º mdEBD-USB.BAS Version 1.02 01/09/2018 º
' ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ
' º º
' º º
' º º
' º ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
' º ³ ³ º
' º ³ ³ º
' º ³ ³ º
' º ³ Copyright (c) 2018, maurits@dijkens.com ³ º
' º ³ ³ º
' º ³ ³ º
' º ³ ³ º
' º ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
' º º
' ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
'____________________________________________________________________________________________________ CONST
CONST PACKET_BEGIN = &HFA
CONST PACKET_END = &HF8
CONST FALSE = 0
CONST TRUE = NOT FALSE
CONST DAY_SECS = 24 * 60 * 60
CONST KEY_ESC = 27
CONST KEY_UP = 72
CONST KEY_DOWN = 80
CONST KEY_LEFT = 75
CONST KEY_RIGHT = 77
'___________________________________________________________________________________________________ SHARED
DIM SHARED V AS SINGLE
DIM SHARED A AS SINGLE
DIM SHARED Ah AS SINGLE
DIM SHARED Wh AS SINGLE
DIM SHARED cV_set AS INTEGER
DIM SHARED mA_set AS INTEGER
DIM SHARED comPort AS INTEGER
DIM SHARED FontPath AS STRING
DIM SHARED FontX AS INTEGER
DIM SHARED FontY AS INTEGER
DIM SHARED Load AS INTEGER
DIM SHARED heartbeat AS LONG
DIM SHARED winHeight
DIM SHARED Logging AS INTEGER
DIM SHARED Monitor AS INTEGER
DIM SHARED mTitle AS STRING
DIM SHARED lTitle AS STRING
DIM SHARED errorNr AS INTEGER
DIM SHARED EditPos AS INTEGER
DIM SHARED Quit AS INTEGER
'__________________________________________________________________________________________________ PROGRAM
IF Init THEN
Main
END IF
Disconnect
SYSTEM
ErrorHandler:
errorNr = ERR
RESUME NEXT
'__________________________________________________________________________________________________ Init ()
FUNCTION Init ()
$EXEICON:'PROJECTS\mdEBD-USB\mdEBD-USB.ico'
_ICON
qmon = _EXIT
mTitle = "mdEBD-USB+"
_TITLE mTitle
IF CheckCommandLine THEN
ON ERROR GOTO ErrorHandler
FOR comPort = 40 TO 1 STEP -1
errorNr = 0
OPEN "COM" + LTRIM$(STR$(comPort)) + ":9600,E,8,1,BIN,CS0,DS0" FOR RANDOM AS #99
IF errorNr = 0 THEN
CLOSE #99: EXIT FOR
END IF
NEXT comPort
ON ERROR GOTO 0
IF Connect = 0 THEN
SCREEN _NEWIMAGE(57, 12, 0)
COLOR 14, 4: CLS
LOCATE 5, 15, 0: PRINT "ZKETECH EBD-USB+ Not Found !"
cV_set = 0
Init = FALSE
ELSE
Init = TRUE
heartbeat = -1
Load = FALSE
Quit = FALSE
mTitle = mTitle + " (COM" + LTRIM$(STR$(comPort)) + ")"
lTitle = SPACE$(20) + "LOAD " + mTitle
mTitle = SPACE$(20) + "MONITOR " + mTitle
IF Monitor THEN
desktop& = _SCREENIMAGE
MaxScreenX& = _WIDTH(desktop&)
MaxScreenY& = _HEIGHT(desktop&)
_FREEIMAGE desktop&
SCREEN 12
SCREEN _NEWIMAGE(800, 200, 256)
_SCREENMOVE (MaxScreenX& - _WIDTH) / 2, (MaxScreenY& - _HEIGHT) / 2
$RESIZE:ON
tmp& = _RESIZE
FontPath = ENVIRON$("SYSTEMROOT") + "\fonts\lucon.ttf"
ELSE
EditPos = 7
SCREEN _NEWIMAGE(65, winHeight, 0)
COLOR 11, 1: CLS
LOCATE 1, 1, 0: PRINT " Date Time Volt Amp Watt Ah Wh";
ScreenStatus
END IF
END IF
ELSE
Init = FALSE
END IF
END FUNCTION
'_____________________________________________________________________________________________________ Main
SUB Main
DO
ret% = CheckReceive
key$ = INKEY$
IF Monitor THEN
IF _RESIZE THEN
IF _RESIZEHEIGHT > 22 THEN
SCREEN _NEWIMAGE(_RESIZEWIDTH, _RESIZEHEIGHT, 256)
ELSE
SCREEN _NEWIMAGE(_RESIZEWIDTH, 22, 256)
END IF
COLOR 2, 0
CLS
font& = _LOADFONT(FontPath, _WIDTH / 4.1, "MONOSPACE")
_FONT font&
FontX = (_WIDTH - _FONTWIDTH * 6.8) / 2
FontY = (_HEIGHT - _FONTHEIGHT) / 2 + .07 * _FONTHEIGHT
_PRINTSTRING (FontX + 1.75 * _FONTWIDTH, FontY), "."
_PRINTSTRING (FontX + 5.7 * _FONTWIDTH, FontY), "V"
ScreenLog FALSE
END IF
IF _EXIT THEN
Quit = 2 * TRUE
END IF
ELSE
IF Load THEN
IF _EXIT THEN
StopLoad: Quit = TRUE
END IF
tDiff! = (DAY_SECS + TIMER - lastTimer!) MOD DAY_SECS
IF tDiff! >= 60 THEN
SendHeartbeat: lastTimer! = TIMER
ELSE
AdjustLoad TRUE
END IF
END IF
SELECT CASE key$
CASE "l", "L"
IF Load THEN
StopLoad
ELSEIF Quit = FALSE THEN
StartLoad: lastTimer! = TIMER
END IF
CASE "m", "M"
IF Load THEN
StopLoad
END IF
CASE CHR$(KEY_ESC)
IF Load THEN
StopLoad
ELSEIF Quit THEN
Quit = FALSE: ScreenStatus
ELSE
Quit = TRUE
END IF
CASE "y", "Y"
IF Quit THEN
Quit = 2 * TRUE
END IF
CASE "n", "N"
IF Quit THEN
Quit = FALSE: ScreenStatus
END IF
CASE ELSE
IF LEN(key$) > 1 AND Quit = FALSE THEN
EditSet ASC(RIGHT$(key$, 1))
END IF
END SELECT
IF _EXIT THEN
Quit = TRUE
END IF
IF Quit THEN
ScreenStatus
END IF
END IF
LOOP UNTIL Quit = 2 * TRUE
END SUB
'__________________________________________________________________________________________ EditSet (ckey%)
SUB EditSet (ckey%)
SELECT CASE ckey%
CASE KEY_UP
SELECT CASE EditPos
CASE 1 TO 3
cV_set = cV_set + 10 ^ (3 - EditPos)
IF cV_set > 2100 THEN cV_set = 2100
CASE 4 TO 7
mA_set = mA_set + 10 ^ (7 - EditPos)
IF mA_set > 4000 THEN mA_set = 4000
CASE ELSE
END SELECT
CASE KEY_DOWN
SELECT CASE EditPos
CASE 1 TO 3
cV_set = cV_set - 10 ^ (3 - EditPos)
IF cV_set < 10 THEN cV_set = 10
CASE 4 TO 7
mA_set = mA_set - 10 ^ (7 - EditPos)
IF mA_set < 1 THEN mA_set = 1
CASE ELSE
END SELECT
CASE KEY_LEFT
IF EditPos > 1 THEN EditPos = EditPos - 1
CASE KEY_RIGHT
IF EditPos < 7 THEN EditPos = EditPos + 1
CASE ELSE
END SELECT
ScreenStatus
END SUB
'__________________________________________________________________________________________ CheckReceive ()
FUNCTION CheckReceive ()
STATIC receivedBytes$
STATIC prevTimer AS SINGLE
DIM byte AS STRING * 1
CheckReceive = FALSE
DO WHILE LOC(1) <> 0
GET #1, , byte
IF ASC(byte) = PACKET_BEGIN THEN
receivedBytes$ = ""
ELSEIF ASC(byte) = PACKET_END THEN
IF LEN(receivedBytes$) > 4 THEN
IF V = 0 AND A = 0 THEN
Ah = 0
Wh = 0
prevTimer = TIMER
END IF
V = (&HF0 * ASC(MID$(receivedBytes$, 4, 1)) + ASC(MID$(receivedBytes$, 5, 1))) / 1000
A = (&HF0 * ASC(MID$(receivedBytes$, 2, 1)) + ASC(MID$(receivedBytes$, 3, 1))) / 10000
tDiff! = (DAY_SECS + TIMER - prevTimer) MOD DAY_SECS
Ah = Ah + A * tDiff! / 3600
Wh = Wh + V * A * tDiff! / 3600
prevTimer = TIMER
IF ASC(MID$(receivedBytes$, 1, 1)) = &HA THEN
Load = TRUE
ELSE
Load = FALSE
END IF
ScreenLog TRUE
CheckReceive = TRUE
END IF
ELSE
receivedBytes$ = receivedBytes$ + byte
END IF
LOOP
END FUNCTION
'_______________________________________________________________________________________________ Connect ()
FUNCTION Connect ()
IF comPort > 0 THEN
OPEN "COM" + LTRIM$(STR$(comPort)) + ":9600,E,8,1,BIN,CS0,DS0" FOR RANDOM AS #1
'FA 05 00 00 00 00 00 00 05 F8
send$ = CHR$(5) + STRING$(6, 0)
SendPacket send$
IF Logging THEN
datetime$ = "_" + RIGHT$(DATE$, 4) + LEFT$(DATE$, 2) + MID$(DATE$, 4, 2)
datetime$ = datetime$ + "_" + LEFT$(TIME$, 2) + MID$(TIME$, 4, 2) + RIGHT$(TIME$, 2)
OPEN "mdEBD-USB" + datetime$ + ".log" FOR OUTPUT AS #2
OPEN "mdEBD-USB" + datetime$ + ".csv" FOR OUTPUT AS #3
PRINT #3, "Rec,Date,Time,Voltage,Current,Power,Ah,Wh,Vset,Aset,Load"
END IF
END IF
Connect = comPort
END FUNCTION
'_______________________________________________________________________________________________ Disconnect
SUB Disconnect
IF cV_set > 0 THEN
'FA 06 00 00 00 00 00 00 06 F8
send$ = CHR$(6) + STRING$(6, 0)
SendPacket send$
CLOSE
ELSE
BEEP
LOCATE 11, 17: COLOR 15: PRINT "Press any key to exit ...";
DO
LOOP UNTIL INKEY$ <> "" OR _EXIT
END IF
END SUB
'________________________________________________________________________________________________ StartLoad
SUB StartLoad
'FA 01 00 0A 00 64 00 00 6F F8 0010mA (00 0A) 0100cV (00 64)
send$ = CHR$(1) + CHR$(mA_set \ &HF0) + CHR$(mA_set MOD &HF0)
send$ = send$ + CHR$(cV_set \ &HF0) + CHR$(cV_set MOD &HF0) + STRING$(2, 0)
SendPacket send$
heartbeat = 0
AdjustLoad FALSE
ScreenStatus
BEEP
END SUB
'_____________________________________________________________________________________ AdjustLoad (update%)
SUB AdjustLoad (update%)
STATIC LastAdjusted AS SINGLE
STATIC last_cV_set AS INTEGER
STATIC last_mA_set AS INTEGER
tDiff! = (DAY_SECS + TIMER - LastAdjusted) MOD DAY_SECS
IF Load AND tDiff! > .5 AND (cV_set <> last_cV_set OR mA_set <> last_mA_set) THEN
IF update% THEN
'FA 07 00 C8 00 64 00 00 AB F8 0200mA 0100cV
send$ = CHR$(7) + CHR$(mA_set \ &HF0) + CHR$(mA_set MOD &HF0)
send$ = send$ + CHR$(cV_set \ &HF0) + CHR$(cV_set MOD &HF0) + STRING$(2, 0)
SendPacket send$
END IF
LastAdjusted = TIMER
last_cV_set = cV_set
last_mA_set = mA_set
END IF
END SUB
'_________________________________________________________________________________________________ StopLoad
SUB StopLoad
'FA 02 00 00 00 00 00 00 02 F8
send$ = CHR$(2) + STRING$(6, 0)
SendPacket send$
heartbeat = -1
ScreenStatus
BEEP
END SUB
'____________________________________________________________________________________________ SendHeartbeat
SUB SendHeartbeat
'FA 0A 00 01 00 00 00 00 0B F8
heartbeat = heartbeat + 1
send$ = CHR$(&H0A) + CHR$(heartbeat \ &HF0) + CHR$(heartbeat MOD &HF0)
send$ = send$ + STRING$(4, 0)
SendPacket send$
END SUB
'_____________________________________________________________________________________ SendPacket (packet$)
SUB SendPacket (packet$)
STATIC lastSend AS SINGLE
crc% = 0
FOR char% = 1 TO LEN(packet$)
crc% = crc% XOR ASC(MID$(packet$, char%, 1))
NEXT char%
packet$ = CHR$(PACKET_BEGIN) + packet$ + CHR$(crc% MOD &HF0) + CHR$(PACKET_END)
DO WHILE (DAY_SECS + TIMER - lastSend) MOD DAY_SECS < .1
LOOP
lastSend = TIMER
PUT #1, , packet$
ret% = CheckReceive
END SUB
'______________________________________________________________________________________________ ScreenTitle
SUB ScreenTitle
titleV$ = LTRIM$(STR$(V))
decimal% = INSTR(titleV$, ".")
IF decimal% = 0 THEN
titleV$ = titleV$ + ".000"
ELSEIF decimal% = LEN(titleV$) - 1 THEN
titleV$ = titleV$ + "00"
ELSEIF decimal% = LEN(titleV$) - 2 THEN
titleV$ = titleV$ + "0"
END IF
IF decimal% = 1 THEN titleV$ = "0" + titleV$
titleV$ = RIGHT$(" " + titleV$ + "V ", 9)
titleA$ = LTRIM$(STR$(A))
decimal% = INSTR(titleA$, ".")
IF decimal% = 0 THEN
titleA$ = titleA$ + ".0000"
ELSEIF decimal% = LEN(titleA$) - 1 THEN
titleA$ = titleA$ + "000"
ELSEIF decimal% = LEN(titleA$) - 2 THEN
titleA$ = titleA$ + "00"
ELSEIF decimal% = LEN(titleA$) - 3 THEN
titleA$ = titleA$ + "0"
END IF
IF decimal% = 1 THEN titleA$ = "0" + titleA$
titleA$ = RIGHT$(" " + titleA$ + "A ", 9)
IF NOT Load THEN
MID$(mTitle, 1, 18) = titleV$ + titleA$
_TITLE mTitle
ELSE
MID$(lTitle, 1, 18) = titleV$ + titleA$
_TITLE lTitle
END IF
END SUB
'_____________________________________________________________________________________________ ScreenStatus
SUB ScreenStatus
setV$ = LTRIM$(STR$(cV_set / 100!))
decimal% = INSTR(setV$, ".")
IF decimal% = 0 THEN
setV$ = setV$ + ".00"
ELSEIF decimal% = LEN(setV$) - 1 THEN
setV$ = setV$ + "0"
END IF
IF decimal% = 1 THEN setV$ = "0" + setV$
setV$ = RIGHT$(" " + setV$ + " V ", 9)
setA$ = LTRIM$(STR$(mA_set / 1000!))
decimal% = INSTR(setA$, ".")
IF decimal% = 0 THEN
setA$ = setA$ + ".000"
ELSEIF decimal% = LEN(setA$) - 1 THEN
setA$ = setA$ + "00"
ELSEIF decimal% = LEN(setA$) - 2 THEN
setA$ = setA$ + "0"
END IF
IF decimal% = 1 THEN setA$ = "0" + setA$
setA$ = RIGHT$(" " + setA$ + " A ", 9)
LOCATE winHeight, 1, 0
IF NOT Load THEN
COLOR 15, 3: PRINT " MONITOR"; SPACE$(24);
IF NOT Quit THEN PRINT SPACE$(14); "L=Load <esc>=Exit ";
ELSE
COLOR 30, 4: PRINT " LOAD ";
COLOR 15, 3: PRINT SPACE$(24);
IF NOT Quit THEN PRINT SPACE$(14); "M=Mon <esc>=Mon ";
END IF
LOCATE , 23: COLOR 1, 3: PRINT setV$;
COLOR 4: PRINT setA$;
IF Quit THEN
LOCATE , 42: COLOR 14, 4: PRINT " Exit program (y/n) ? ";
ELSE
SELECT CASE EditPos
CASE 1
LOCATE , 25, 1, 7, 8
CASE 2 TO 3
LOCATE , 25 + EditPos, 1, 7, 8
CASE 4
LOCATE , 29 + EditPos, 1, 7, 8
CASE 5 TO 7
LOCATE , 30 + EditPos, 1, 7, 8
CASE ELSE
END SELECT
END IF
ScreenTitle
END SUB
'_____________________________________________________________________________________ ScreenLog (newData%)
SUB ScreenLog (newData%)
IF Monitor THEN
Vprint$ = Format$("##.###", V)
_PRINTSTRING (FontX, FontY), MID$(Vprint$, 1, 2)
_PRINTSTRING (FontX + 2.5 * _FONTWIDTH, FontY), MID$(Vprint$, 4, 3)
ScreenTitle
ELSE
STATIC rowPos AS INTEGER
STATIC csv_row AS LONG
VIEW PRINT 2 TO winHeight - 1
IF rowPos < 1 THEN
LOCATE 2, 1, 0
ELSE
LOCATE rowPos, 65, 0
COLOR 7, 1: PRINT
END IF
LOCATE , 2
highlight% = -Load * 8
COLOR 7 + highlight%, 1
PRINT USING "\ \ \ \"; DATE$; TIME$;
COLOR 9 - highlight%, 8 - SGN(highlight%): LOCATE , 23: PRINT USING " ##.### "; V;
COLOR 12 - highlight%, 8 - SGN(highlight%): LOCATE , 32: PRINT USING " #.#### "; A;
COLOR 3 + highlight%, 1: LOCATE , 40: PRINT USING " ##.#### "; V * A;
COLOR 10, 1: LOCATE , 48: PRINT USING " ##.#### "; Ah;
COLOR 13, 1: LOCATE , 56: PRINT USING " ###.####"; Wh;
rowPos = CSRLIN
VIEW PRINT
ScreenStatus
END IF
IF Logging AND newData% THEN
PRINT #2, USING "\ \ \ \ "; DATE$; TIME$;
PRINT #2, USING "##.### V #.#### A ##.#### W "; V; A; V * A;
PRINT #2, USING "##.#### Ah ###.#### Wh "; Ah; Wh;
PRINT #2, USING "Set: ##.## V #.### A"; cV_set / 100; mA_set / 1000;
csv_row = csv_row + 1
PRINT #3, csv_row; ","; DATE$; ","; TIME$; ","; V; ","; A; ","; V * A; ",";
PRINT #3, Ah; ","; Wh; ","; cV_set / 100; ","; mA_set / 1000;
IF Load THEN
PRINT #2, " LOAD"
PRINT #3, ",LOAD"
ELSE
PRINT #2, ""
PRINT #3, ""
END IF
END IF
END SUB
'______________________________________________________________________________________ CheckCommandLine ()
FUNCTION CheckCommandLine ()
cV_set = 100
mA_set = 100
Logging = FALSE
Monitor = FALSE
CheckCommandLine = TRUE
winHeight = 25
IF COMMAND$ <> "" THEN
cmdLine$ = UCASE$(COMMAND$)
spacePos% = 1
FOR char% = 1 TO LEN(cmdLine$)
SELECT CASE MID$(cmdLine$, char%, 1)
CASE " "
spacePos% = char% + 1
CASE "V"
cV_cmd% = VAL(MID$(cmdLine$, spacePos%, char% - spacePos%)) * 100
IF cV_cmd% > 0 AND cV_cmd% < 2100 THEN cV_set = cV_cmd%
CASE "A"
mAc_cmd% = VAL(MID$(cmdLine$, spacePos%, char% - spacePos%)) * 1000
IF mAc_cmd% > 0 AND mAc_cmd% < 4000 THEN mA_set = mAc_cmd%
CASE "/", "-"
IF MID$(cmdLine$ + SPACE$(4), char% + 1, 3) = "LOG" THEN
Logging = TRUE
ELSEIF MID$(cmdLine$ + SPACE$(4), char% + 1, 3) = "MON" THEN
Monitor = TRUE
ELSEIF MID$(cmdLine$ + SPACE$(2), char% + 1, 1) = "R" THEN
r% = VAL(MID$(cmdLine$, char% + 2))
IF r% < 1 THEN
winHeight = 3
ELSEIF r% > 62 THEN
winHeight = 64
ELSE
winHeight = r% + 2
END IF
ELSE
SCREEN _NEWIMAGE(71, 14, 0)
COLOR 11, 1: CLS
LOCATE 3, 5, 0
PRINT "Usage: ";
COLOR 14: PRINT "mdEBD-USB.EXE ";
COLOR 15: PRINT "[##.##V] [#.###A] [/r##] | /mon [/log]";
LOCATE 5, 5
COLOR 11: PRINT "Where: ";
COLOR 15: PRINT "[##.##V]";
COLOR 11: PRINT " = Min. Load Voltage between 0.10V and 21.00V";
LOCATE 6, 13
COLOR 15: PRINT "[#.###A] ";
COLOR 11: PRINT "= Load Current between 0.001A and 4.000A";
LOCATE 7, 13
COLOR 15: PRINT "[/r##]";
COLOR 11: PRINT " = Number of logging data rows between 1 and 62";
LOCATE 8, 13
COLOR 15: PRINT "/mon ";
COLOR 11: PRINT "= Voltage Monitor mode";
LOCATE 9, 13
COLOR 15: PRINT "[/log] ";
COLOR 11: PRINT "= Enable logging to text and csv";
cV_set = 0
CheckCommandLine = FALSE
END IF
CASE ELSE
END SELECT
NEXT char%
END IF
END FUNCTION
'_____________________________________________________________________________ Format$ (template$, value##)
FUNCTION Format$ (template$, value##)
PrevSource& = _SOURCE
PrevDest& = _DEST
tImage& = _NEWIMAGE(1000, 1, 0)
_DEST tImage&
_SOURCE tImage&
PRINT USING template$; value##;
retValue$ = ""
FOR c% = 1 TO POS(1) - 1
retValue$ = retValue$ + CHR$(SCREEN(1, c%))
NEXT c%
_DEST PrevDest&
_SOURCE PrevSource&
_FREEIMAGE tImage&
Format$ = retValue$
END FUNCTION
@mdijkens
Copy link
Author

mdijkens commented Apr 7, 2018

App (ugly but easy) can be downloaded here: http://www.dijkens.com/mdFluke/mdEBD-USB.exe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment