Teensy 2.0 and 3.2 responding differently to Python serial communication

Status
Not open for further replies.
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:
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
 
Status
Not open for further replies.
Back
Top