01-06-2026, 07:44 PM
Oh. Linux Mint. Hmmmm. I took AI to help me with this.
1) First, check that Mint sees the adapter and what its name is
Find out the device after connecting
In the terminal:
dmesg | tail -n 50 (after connecting the adapter)
or: ls -l /dev/ttyUSB* /dev/ttyACM*
For a stable name, it is best to use a symlink:
ls -l /dev/serial/by-id/
The number ttyUSB0/1/... may change after a reboot or a connection, the by-id is usually stable.
2) Permissions: without the dialout group you can't get anywhere
Typical error on Mint/Ubuntu: the program doesn't have permission to open /dev/ttyUSB0.
Check:
ls -l /dev/ttyUSB0 (or specific port)
If the group is dialout, add the user:
sudo usermod -a -G dialout $USER
Then log out and log in (or reboot) for the group to really take effect.
3) Verify that the port is not occupied by anything else
If you are running screen, minicom, IDE, Modbus Poll in Wine, whatever... QB64PE is then "reading silent".
4) Quick non-QB64 line test (just a sanity check)
To verify that the port can at least be opened and is set up:
picocom -b 9600 /dev/ttyUSB0
or: screen /dev/ttyUSB0 9600
MODBUS is binary, so don't expect "readable characters". The point is just that you open the port without error and the adapter behaves sensibly.
5) QB64PE on Linux: edit port opening to /dev/ttyUSB0 + set up link via stty
Officially QB64 says it doesn't support OPEN "COMn:..." syntax for Linux, but on Linux with USB-serial converters, in practice it is often used to open /dev/ttyUSB0 directly as a file.
Here you have a ready-made "Linux Mint" skeleton. Two things are important:
I open dev$ (e.g. /dev/ttyUSB0 or /dev/serial/by-id/...)
Before opening, I call stty so that the port has 9600,N,8,1 and soft flow control crap is not enabled
What is the most likely problem now (based on what you described earlier)
When only TX is lit and RX never even blinks, it almost always falls into one of these categories on Linux:
the program actually opens a different port than the one the adapter is on (that's why I recommend /dev/serial/by-id/...)
you don't have rights to /dev/ttyUSB0 (dialout)
A/B are swapped or the module is not powered (RS485 doesn't power it - it must be VIN)
the port is being held by another process (lsof)
From Mint, send me the literal outputs of these commands:
ls -l /dev/serial/by-id/
ls -l /dev/ttyUSB*
groups
lsof /dev/ttyUSB0 (or the actual port)
And the log from QB64PE: TX temps: and RX temps: lines.
Try also this version:
1) First, check that Mint sees the adapter and what its name is
Find out the device after connecting
In the terminal:
dmesg | tail -n 50 (after connecting the adapter)
or: ls -l /dev/ttyUSB* /dev/ttyACM*
For a stable name, it is best to use a symlink:
ls -l /dev/serial/by-id/
The number ttyUSB0/1/... may change after a reboot or a connection, the by-id is usually stable.
2) Permissions: without the dialout group you can't get anywhere
Typical error on Mint/Ubuntu: the program doesn't have permission to open /dev/ttyUSB0.
Check:
ls -l /dev/ttyUSB0 (or specific port)
If the group is dialout, add the user:
sudo usermod -a -G dialout $USER
Then log out and log in (or reboot) for the group to really take effect.
3) Verify that the port is not occupied by anything else
If you are running screen, minicom, IDE, Modbus Poll in Wine, whatever... QB64PE is then "reading silent".
4) Quick non-QB64 line test (just a sanity check)
To verify that the port can at least be opened and is set up:
picocom -b 9600 /dev/ttyUSB0
or: screen /dev/ttyUSB0 9600
MODBUS is binary, so don't expect "readable characters". The point is just that you open the port without error and the adapter behaves sensibly.
5) QB64PE on Linux: edit port opening to /dev/ttyUSB0 + set up link via stty
Officially QB64 says it doesn't support OPEN "COMn:..." syntax for Linux, but on Linux with USB-serial converters, in practice it is often used to open /dev/ttyUSB0 directly as a file.
Here you have a ready-made "Linux Mint" skeleton. Two things are important:
I open dev$ (e.g. /dev/ttyUSB0 or /dev/serial/by-id/...)
Before opening, I call stty so that the port has 9600,N,8,1 and soft flow control crap is not enabled
Code: (Select All)
Option _Explicit
' Linux Mint 22 (Wilma) + USB-RS485: recommended approach
'
' Key differences vs Windows:
' - Use /dev/ttyUSB0 or /dev/serial/by-id/... instead of COM1
' - Ensure user has permission (dialout group)
' - Configure port via stty before opening it in QB64PE
'
' SEND FOR TUNING (paste into forum/chat):
' - output of: ls -l /dev/ttyUSB* /dev/serial/by-id/
' - output of: groups
' - output of: ls -l /dev/ttyUSB0 (or your actual device)
' - output of: lsof /dev/ttyUSB0
' - TX/RX hexdumps from this program
Dim Shared Byte1 As String * 1
' --- EDIT THIS: best is /dev/serial/by-id/xxxx (stable), otherwise /dev/ttyUSB0
Dim dev$: dev$ = "/dev/ttyUSB0"
Dim pollHz!: pollHz! = 2
Dim showFrames%: showFrames% = -1
Dim timeout!: timeout! = 1.0
Dim h%: h% = FreeFile
Dim req$, resp$
Dim slave%: slave% = 1
Dim i%, rawS%, rawU&, t!, r!
Dim cmd$
' 1) Configure port using stty
' raw 9600 8N1, no flow control, no echo
cmd$ = "stty -F " + dev$ + " 9600 cs8 -cstopb -parenb -ixon -ixoff -crtscts raw -echo"
Shell cmd$
' 2) Open the device as a binary file
' LEN=1 ensures byte-accurate GET/PUT behavior (no padded records).
Open dev$ For Binary As #h% Len = 1
Print "Opened "; dev$; " (should be USB-RS485). ESC exits."
Print
Do
If InKey$ = Chr$(27) Then Exit Do
' Read 8 temperatures 0x0000..0x0007
SerialFlush h%
req$ = ModbusBuildRead$(slave%, &H0000, 8)
If showFrames% Then Print "TX temps: "; HexDump$(req$)
SerialWrite h%, req$
resp$ = SerialReadFrame$(h%, slave%, timeout!)
If showFrames% Then Print "RX temps: "; HexDump$(resp$)
If Len(resp$) = 0 Then
Print "No MODBUS reply. If adapter RX LED never blinks: wiring/power/A-B swap/address."
Print
_Limit pollHz!
GoTo ContinueLoop
End If
If ModbusCheckCrc%(resp$) = 0 Then
Print "CRC FAIL."
Print
_Limit pollHz!
GoTo ContinueLoop
End If
If Asc(resp$, 2) <> &H03 Then
Print "Unexpected function code:"; Asc(resp$, 2)
Print
_Limit pollHz!
GoTo ContinueLoop
End If
If Asc(resp$, 3) <> 16 Then
Print "Unexpected bytecount:"; Asc(resp$, 3); " (expected 16)"
Print
_Limit pollHz!
GoTo ContinueLoop
End If
For i% = 0 To 7
rawS% = GetS16BE%(resp$, 4 + i% * 2)
t! = rawS% / 10!
Print Using "CH# = ####.# C"; i%; t!
Next i%
Print "----------------------"
Print
ContinueLoop:
_Limit pollHz!
Loop
Close #h%
End
Function Crc16Modbus& (s$)
Dim crc&, i%, j%
crc& = &HFFFF&
For i% = 1 To Len(s$)
crc& = crc& Xor Asc(s$, i%)
For j% = 1 To 8
If (crc& And 1) Then
crc& = (crc& \ 2) Xor &HA001&
Else
crc& = crc& \ 2
End If
Next j%
Next i%
Crc16Modbus& = (crc& And &HFFFF&)
End Function
Function U16BE$ (v&)
U16BE$ = Chr$((v& \ 256) And &HFF) + Chr$(v& And &HFF)
End Function
Function HexDump$ (s$)
Dim i%, out$
out$ = ""
For i% = 1 To Len(s$)
out$ = out$ + Right$("0" + Hex$(Asc(s$, i%)), 2) + " "
Next i%
HexDump$ = out$
End Function
Sub SerialFlush (h%)
While Loc(h%) > 0
Get #h%, , Byte1
Wend
End Sub
Sub SerialWrite (h%, s$)
Dim i%
For i% = 1 To Len(s$)
Byte1 = Mid$(s$, i%, 1)
Put #h%, , Byte1
Next i%
End Sub
Function ModbusBuildRead$ (slave%, reg&, count%)
Dim req$, crc&
req$ = Chr$(slave% And &HFF) + Chr$(&H03) + U16BE$(reg&) + U16BE$(count%)
crc& = Crc16Modbus&(req$)
req$ = req$ + Chr$(crc& And &HFF) + Chr$((crc& \ 256) And &HFF)
ModbusBuildRead$ = req$
End Function
Function ModbusCheckCrc% (frame$)
Dim n%, crcRx&, crcCalc&
n% = Len(frame$)
If n% < 5 Then ModbusCheckCrc% = 0: Exit Function
crcRx& = Asc(frame$, n% - 1) + 256& * Asc(frame$, n%)
crcCalc& = Crc16Modbus&(Left$(frame$, n% - 2))
ModbusCheckCrc% = (crcRx& = crcCalc&)
End Function
Function GetU16BE& (s$, position%)
Dim hi&, lo&
hi& = Asc(s$, position%) And &HFF
lo& = Asc(s$, position% + 1) And &HFF
GetU16BE& = hi& * 256& + lo&
End Function
Function GetS16BE% (s$, position%)
Dim v&
v& = GetU16BE&(s$, position%)
If v& >= 32768& Then v& = v& - 65536&
GetS16BE% = v&
End Function
Function SerialReadFrame$ (h%, expectedSlave%, timeoutSec!)
' Minimal frame reader for function 03 only:
' Accumulate bytes and try to find a valid CRC frame with:
' [addr][03][bytecount][...data...][crcLo][crcHi]
Dim t0!, buf$, i%, n%, addr%, fn%, bc%, frameLen%
Dim candidate$
t0! = Timer
buf$ = ""
Do
While Loc(h%) > 0
Get #h%, , Byte1
buf$ = buf$ + Byte1
If Len(buf$) > 512 Then buf$ = Right$(buf$, 512)
Wend
n% = Len(buf$)
If n% >= 5 Then
For i% = 1 To n% - 4
addr% = Asc(buf$, i%) And &HFF
If addr% <> (expectedSlave% And &HFF) Then GoTo NextI
fn% = Asc(buf$, i% + 1) And &HFF
If fn% <> &H03 Then GoTo NextI
bc% = Asc(buf$, i% + 2) And &HFF
frameLen% = 5 + bc%
If (i% + frameLen% - 1) <= n% Then
candidate$ = Mid$(buf$, i%, frameLen%)
If ModbusCheckCrc%(candidate$) Then
SerialReadFrame$ = candidate$
Exit Function
End If
End If
NextI:
Next i%
End If
If (Timer - t0!) >= timeoutSec! Then Exit Do
Loop
SerialReadFrame$ = ""
End Function
What is the most likely problem now (based on what you described earlier)
When only TX is lit and RX never even blinks, it almost always falls into one of these categories on Linux:
the program actually opens a different port than the one the adapter is on (that's why I recommend /dev/serial/by-id/...)
you don't have rights to /dev/ttyUSB0 (dialout)
A/B are swapped or the module is not powered (RS485 doesn't power it - it must be VIN)
the port is being held by another process (lsof)
From Mint, send me the literal outputs of these commands:
ls -l /dev/serial/by-id/
ls -l /dev/ttyUSB*
groups
lsof /dev/ttyUSB0 (or the actual port)
And the log from QB64PE: TX temps: and RX temps: lines.
Try also this version:
Code: (Select All)
Option _Explicit
' Linux Mint 22 (Wilma) + USB-RS485: recommended approach
'
' Key differences vs Windows:
' - Use /dev/ttyUSB0 or /dev/serial/by-id/... instead of COM1
' - Ensure user has permission (dialout group)
' - Configure port via stty before opening it in QB64PE
'
' SEND FOR TUNING (paste into forum/chat):
' - output of: ls -l /dev/ttyUSB* /dev/serial/by-id/
' - output of: groups
' - output of: ls -l /dev/ttyUSB0 (or your actual device)
' - output of: lsof /dev/ttyUSB0
' - TX/RX hexdumps from this program
Dim Shared Byte1 As String * 1
' --- EDIT THIS: best is /dev/serial/by-id/xxxx (stable), otherwise /dev/ttyUSB0
Dim dev$: dev$ = "/dev/ttyUSB0"
Dim pollHz!: pollHz! = 2
Dim showFrames%: showFrames% = -1
Dim timeout!: timeout! = 1.0
Dim h%: h% = FreeFile
Dim req$, resp$
Dim slave%: slave% = 1
Dim i%, rawS%, rawU&, t!, r!
Dim cmd$
' 1) Configure port using stty
' raw 9600 8N1, no flow control, no echo
cmd$ = "stty -F " + dev$ + " 9600 cs8 -cstopb -parenb -ixon -ixoff -crtscts raw -echo"
Shell cmd$
' 2) Open the device as a binary file
' LEN=1 ensures byte-accurate GET/PUT behavior (no padded records).
Open dev$ For Binary As #h% Len = 1
Print "Opened "; dev$; " (should be USB-RS485). ESC exits."
Print
' ------------------------------------------------------------
' STEP 0: Read station address / DIP settings via broadcast read of 0x00FD
' Protocol gives example request: FF 03 00 FD 00 01 00 24
' We build it dynamically (CRC), send it, and accept ANY responding slave.
' ------------------------------------------------------------
SerialFlush h%
req$ = ModbusBuildRead$(255, &H00FD, 1) ' 255 = FF broadcast
If showFrames% Then Print "TX addr (FF 00FD): "; HexDump$(req$)
SerialWrite h%, req$
resp$ = SerialReadFrame$(h%, 0, timeout!) ' expectedSlave=0 => accept any slave in reply
If showFrames% Then Print "RX addr: "; HexDump$(resp$)
If Len(resp$) > 0 And ModbusCheckCrc%(resp$) Then
slave% = Asc(resp$, 1) And &HFF
Print "Detected station address from reply ="; slave%
Print
Else
Print "Could not read station address (00FD)."
Print "If RX LED never blinks: check power (VIN), A/B swap, correct /dev/ttyUSBx, permissions."
Print
' Keep slave% as configured (default 1) and continue anyway
End If
Do
If InKey$ = Chr$(27) Then Exit Do
' Read 8 temperatures 0x0000..0x0007
SerialFlush h%
req$ = ModbusBuildRead$(slave%, &H0000, 8)
If showFrames% Then Print "TX temps: "; HexDump$(req$)
SerialWrite h%, req$
resp$ = SerialReadFrame$(h%, slave%, timeout!)
If showFrames% Then Print "RX temps: "; HexDump$(resp$)
If Len(resp$) = 0 Then
Print "No MODBUS reply. If adapter RX LED never blinks: wiring/power/A-B swap/address."
Print
_Limit pollHz!
GoTo ContinueLoop
End If
If ModbusCheckCrc%(resp$) = 0 Then
Print "CRC FAIL."
Print
_Limit pollHz!
GoTo ContinueLoop
End If
If Asc(resp$, 2) <> &H03 Then
Print "Unexpected function code:"; Asc(resp$, 2)
Print
_Limit pollHz!
GoTo ContinueLoop
End If
If Asc(resp$, 3) <> 16 Then
Print "Unexpected bytecount:"; Asc(resp$, 3); " (expected 16)"
Print
_Limit pollHz!
GoTo ContinueLoop
End If
For i% = 0 To 7
rawS% = GetS16BE%(resp$, 4 + i% * 2)
t! = rawS% / 10!
Print Using "CH# = ####.# C"; i%; t!
Next i%
Print "----------------------"
Print
ContinueLoop:
_Limit pollHz!
Loop
Close #h%
End
Function Crc16Modbus& (s$)
Dim crc&, i%, j%
crc& = &HFFFF&
For i% = 1 To Len(s$)
crc& = crc& Xor Asc(s$, i%)
For j% = 1 To 8
If (crc& And 1) Then
crc& = (crc& \ 2) Xor &HA001&
Else
crc& = crc& \ 2
End If
Next j%
Next i%
Crc16Modbus& = (crc& And &HFFFF&)
End Function
Function U16BE$ (v&)
U16BE$ = Chr$((v& \ 256) And &HFF) + Chr$(v& And &HFF)
End Function
Function HexDump$ (s$)
Dim i%, out$
out$ = ""
For i% = 1 To Len(s$)
out$ = out$ + Right$("0" + Hex$(Asc(s$, i%)), 2) + " "
Next i%
HexDump$ = out$
End Function
Sub SerialFlush (h%)
While Loc(h%) > 0
Get #h%, , Byte1
Wend
End Sub
Sub SerialWrite (h%, s$)
Dim i%
For i% = 1 To Len(s$)
Byte1 = Mid$(s$, i%, 1)
Put #h%, , Byte1
Next i%
End Sub
Function ModbusBuildRead$ (slave%, reg&, count%)
Dim req$, crc&
req$ = Chr$(slave% And &HFF) + Chr$(&H03) + U16BE$(reg&) + U16BE$(count%)
crc& = Crc16Modbus&(req$)
req$ = req$ + Chr$(crc& And &HFF) + Chr$((crc& \ 256) And &HFF)
ModbusBuildRead$ = req$
End Function
Function ModbusCheckCrc% (frame$)
Dim n%, crcRx&, crcCalc&
n% = Len(frame$)
If n% < 5 Then ModbusCheckCrc% = 0: Exit Function
crcRx& = Asc(frame$, n% - 1) + 256& * Asc(frame$, n%)
crcCalc& = Crc16Modbus&(Left$(frame$, n% - 2))
ModbusCheckCrc% = (crcRx& = crcCalc&)
End Function
Function GetU16BE& (s$, position%)
Dim hi&, lo&
hi& = Asc(s$, position%) And &HFF
lo& = Asc(s$, position% + 1) And &HFF
GetU16BE& = hi& * 256& + lo&
End Function
Function GetS16BE% (s$, position%)
Dim v&
v& = GetU16BE&(s$, position%)
If v& >= 32768& Then v& = v& - 65536&
GetS16BE% = v&
End Function
Function SerialReadFrame$ (h%, expectedSlave%, timeoutSec!)
' Minimal frame reader for function 03 only:
' Accumulate bytes and try to find a valid CRC frame with:
' [addr][03][bytecount][...data...][crcLo][crcHi]
Dim t0!, buf$, i%, n%, addr%, fn%, bc%, frameLen%
Dim candidate$
t0! = Timer
buf$ = ""
Do
While Loc(h%) > 0
Get #h%, , Byte1
buf$ = buf$ + Byte1
If Len(buf$) > 512 Then buf$ = Right$(buf$, 512)
Wend
n% = Len(buf$)
If n% >= 5 Then
For i% = 1 To n% - 4
addr% = Asc(buf$, i%) And &HFF
If expectedSlave% <> 0 Then
If addr% <> (expectedSlave% And &HFF) Then GoTo NextI
End If
fn% = Asc(buf$, i% + 1) And &HFF
If fn% <> &H03 Then GoTo NextI
bc% = Asc(buf$, i% + 2) And &HFF
frameLen% = 5 + bc%
If (i% + frameLen% - 1) <= n% Then
candidate$ = Mid$(buf$, i%, frameLen%)
If ModbusCheckCrc%(candidate$) Then
SerialReadFrame$ = candidate$
Exit Function
End If
End If
NextI:
Next i%
End If
If (Timer - t0!) >= timeoutSec! Then Exit Do
Loop
SerialReadFrame$ = ""
End Function

