steve.mcgie
Member
In my current project, the Teensy can have certain settings reprogrammed through a GUI. This is done by sending a long string with the settings over USB serial. The Teensy then parses the settings and writes them to internal EEPROM. The GUI can also send a particular character over serial to query the Teensy, and the Teensy responds with which version of the firmware it's using (this is one of the settings saved to EEPROM).
I wrote the original GUI in Visual Basic, and had everything working fine with both Teensy 2.0 & 3.2. But I'm recently trying to write a new version of it in Python (which I'm admittedly new to). It works fine with the 3.2, and the correct value gets returned. But with the 2.0, a value of 255 gets returned the first time this happens in a given run of the GUI, and all subsequent calls give 0 (due to a timeout). The 2.0 is also bricked at this point, not running its program, and being unable to have new sketches written to it through Teensyduino. To unbrick it, I've had to send it a small sketch that wipes the EEPROM clean, hit the reset button so it actually uploads, then resend the original sketch. My top theory right now is that maybe the 2.0 interprets the special request character as if it was a regular setting, and writes it erroneously to the EEPROM, which creates problems when the sketch next tries to load settings from EEPROM...?
I'm using Arduino v1.0.6, Teensyduino v1.35, Python v3 through Mu v1.0.0, and Visual Studio v12.0.31101.00 Update 4 with .NET v4.7.03062 and Visual Basic 2013.
Does anyone have suggestions as to why the 2.0 and the 3.2 might be reacting differently to this?
Here's the Teensyduino firmware, with some less relevant parts put in pseudocode for brevity:
Here's the relevant Python GUI code:
And for comparison, the original Visual Basic GUI code that the Python code is supposed to mimic:
I wrote the original GUI in Visual Basic, and had everything working fine with both Teensy 2.0 & 3.2. But I'm recently trying to write a new version of it in Python (which I'm admittedly new to). It works fine with the 3.2, and the correct value gets returned. But with the 2.0, a value of 255 gets returned the first time this happens in a given run of the GUI, and all subsequent calls give 0 (due to a timeout). The 2.0 is also bricked at this point, not running its program, and being unable to have new sketches written to it through Teensyduino. To unbrick it, I've had to send it a small sketch that wipes the EEPROM clean, hit the reset button so it actually uploads, then resend the original sketch. My top theory right now is that maybe the 2.0 interprets the special request character as if it was a regular setting, and writes it erroneously to the EEPROM, which creates problems when the sketch next tries to load settings from EEPROM...?
I'm using Arduino v1.0.6, Teensyduino v1.35, Python v3 through Mu v1.0.0, and Visual Studio v12.0.31101.00 Update 4 with .NET v4.7.03062 and Visual Basic 2013.
Does anyone have suggestions as to why the 2.0 and the 3.2 might be reacting differently to this?
Here's the Teensyduino firmware, with some less relevant parts put in pseudocode for brevity:
Code:
#include <EEPROM.h>
const char DETECT_CHAR = '{';
const char TERMINATOR = '~';
const long readTimeout = 15000;
char firmwareVersion=1; // this is the value returned when the DETECT_CHAR is found over Serial
// other local variables representing the settings of the device are initialized here
void setup(){
Serial.begin(9600);
for (int i = 0; i < 1000; i++){
updateSettingVars(i);
}
delay(100);
}
void loop(){
runIteration(); // this runs the normal operation of the device
if (lookForNewSettings()) setup();
delay(10);
}
void updateSettingVars(int whichSetting){
byte settingVal = EEPROM.read(whichSetting)
// the relevant local variable, as determined by whichSetting, is then assigned a value of settingVal
// 'firmwareVersion' is set as part of this process, having been loaded from EEPROM
}
boolean lookForNewSettings(){
boolean newSettingsFound = false;
boolean ranOutOfSerial = false;
boolean detectRequestFound = false;
boolean readHasTimedOut = false;
int whichSetting=0;
if (Serial.available() > 0){
long startTime = millis();
while (!(detectRequestFound || readHasTimedOut || newSettingsFound)){
if (Serial.available() > 0){
char thisReadChar = Serial.read();
if (thisReadChar == DETECT_CHAR){
detectRequestFound = true;
} else if (thisReadChar == TERMINATOR){
newSettingsFound = true;
} else {
EEPROM.write(whichSetting, (byte)thisReadChar);
whichSetting++;
}
} else {
delay (1);
}
if ((millis() - startTime) > readTimeout){
readHasTimedOut = true;
}
}
}
if (newSettingsFound){
for (int i = 0; i < whichSetting; i++){
updateSettingVars(i);
}
for (int i = whichSetting; i < 1023; i++){
EEPROM.write(i, 255);
}
}
if (detectRequestFound){
delay(COMMON_LOOP_DELAY * 3);
byte settingBuffer[COMMON_BUFFER_LENGTH];
settingBuffer[0] = firmwareVersion;
int i = 1;
Serial.write(settingBuffer, 1);
delay(10);
}
return newSettingsFound;
}
Here's the relevant Python GUI code:
Code:
from tkinter import *
from tkinter import ttk
import tkinter as tk
import serial.tools.list_ports as com
import serial
TEENSY_2_0_ID="USB VID:PID=16C0:0483"
TEENSY_3_2_ID="USB VID:PID=16C0:0487"
DETECT_CHAR = "{"
class Application(ttk.Frame):
def __init__(self, master=None):
super(Application, self).__init__(master)
frame1=Frame(self)
frame1.pack(fill=X, side=TOP)
detectButton = ttk.Button(frame1, text='Detect', command=detectButtonPressed)
detectButton.pack()
def detectButtonPressed():
validComPort=False
for thisPort in com.comports():
if (TEENSY_2_0_ID in thisPort.hwid or TEENSY_3_2_ID in thisPort.hwid):
validComPort=True
if validComPort:
ser=serial.Serial(port=getComPortName(), baudrate=9600, timeout=2, write_timeout=2)
ser.write(DETECT_CHAR.encode())
rawResponse=ser.read()
ser.close()
returnedValue=int.from_bytes(rawResponse, "big")
print(returnedValue)
else:
print ("No device detected!")
And for comparison, the original Visual Basic GUI code that the Python code is supposed to mimic:
Code:
Imports System
Imports System.IO
Imports System.IO.Ports
Imports System.Text
Imports System.Management
Imports System.Net.Sockets
Public Class Application
Const DETECT_CHAR As Char = "{"
Dim port As New SerialPort("COM1", 9600)
Private Sub detectButton_Click(sender As Object, e As EventArgs) Handles detectButton.Click
MessageBox.Show(CStr(sendDetectequest()))
End Sub
Public Function sendDetectRequest() As Integer
Dim toReturn As Integer = -1
Dim exceptionCalled As Boolean = False
Dim readBuffer(500) As Byte
Dim readBufferIndex As Integer = 0
If (establishSerialConnection()) Then
Dim serialString As String = ""
Try
port.Write(DETECT_CHAR)
Catch ex As Exception
exceptionCalled = True
End Try
System.Threading.Thread.Sleep(30)
Dim elapsedTime As Integer = 0
If (Not exceptionCalled) Then
Dim found As Boolean = False
While (Not found And elapsedTime < 4000)
Dim newByte As Byte = 0
Try
newByte = port.ReadByte()
Catch
Return -1
End Try
If (newByte <> -1) Then found = True
If (newByte <> -1 And newByte <> 0 And newByte <> 255) Then toReturn = newByte
System.Threading.Thread.Sleep(1)
elapsedTime += 1
End While
If (found) Then
System.Threading.Thread.Sleep(10)
Return toReturn
End If
Try
port.Close()
Catch
End Try
End If
End If
Return -1
End Function
Private Function establishSerialConnection() As Boolean
Dim matchingPort As String = ""
Dim nMatchingPorts As Integer = 0
Dim errorCalled As Boolean = False
Try
Dim searcher As New ManagementObjectSearcher( _
"root\cimv2", _
"SELECT * FROM Win32_SerialPort")
For Each queryObj As ManagementObject In searcher.Get()
Dim closingBracketI As Integer = -1
Dim comName As String = queryObj("Name")
If (comName.Contains("Teensy") And comName.Contains("(COM")) Then
For i As Integer = 0 To (comName.Length - 4)
If (comName.Substring(i, 4) = "(COM") Then
For i2 As Integer = i To (comName.Length - 1)
If (comName.Substring(i2, 1) = ")") Then closingBracketI = i2
Next
matchingPort = comName.Substring(i + 4, closingBracketI - i - 4)
End If
Next
nMatchingPorts += 1
End If
Next
Catch err As ManagementException
errorCalled = True
End Try
Try
port.Close()
Catch ex As Exception
End Try
If (Not errorCalled And nMatchingPorts=1) Then
System.Threading.Thread.Sleep(10)
port.PortName = ("COM" + matchingPort)
port.ReadTimeout = 5000
port.WriteTimeout = 5000
Try
port.Open()
Catch ex As Exception
End Try
End If
Return (Not errorCalled And nMatchingPorts=1)
End Function
End Class