Hi all,
I try make app which send data from Teensy 4.0 to Kivy Python app. Now I send some info data usin serial.print and its ok. I send 30000 values using serial.write and it works for a few readings and then is some error.
I need help with 3 points:
- How to send data correct and as fast as possible?
- How send 30000 or 120000 or 240000 of values?
- How read data in loop without data loss?
Teensy code:
Python Kivy code:
Thanks for any advice!
Michal
I try make app which send data from Teensy 4.0 to Kivy Python app. Now I send some info data usin serial.print and its ok. I send 30000 values using serial.write and it works for a few readings and then is some error.
I need help with 3 points:
- How to send data correct and as fast as possible?
- How send 30000 or 120000 or 240000 of values?
- How read data in loop without data loss?
Teensy code:
Rich (BB code):
#include <Arduino.h>
const int numSamples = 30000; // Number of samples to generate and send
float samples[numSamples];
void generateSineWave() {
float amplitude = random(50, 301) / 100.0; // Random amplitude between 0.5g and 3g
float frequency = 50.0; // 50Hz
float samplingRate = 30000.0; // 30k SPS
for (int i = 0; i < numSamples; i++) {
samples = amplitude * sin(2 * PI * frequency * (i / samplingRate));
}
}
void setup() {
Serial.begin(115200);
randomSeed(analogRead(0));
while (!Serial);
}
void loop() {
if (Serial.available() > 0) {
char receivedChar = Serial.read();
if (receivedChar == 't') {
Serial.println("@"); // Send Analyzer ready message
} else if (receivedChar == 'C') {
delay(10);
if (Serial.available() > 0) {
char command = Serial.read();
if (command == '1') {
Serial.println("Correction1=1.123456"); // Send Correction1 value
} else if (command == '2') {
Serial.println("Correction2=2.12345"); // Send Correction2 value
}
}
} else if (receivedChar == 'r') {
generateSineWave();
Serial.println("D"); // Data start transfer
sendData();
Serial.println("E"); // Data sending end
}
}
}
void sendData() {
int bytesToSend = numSamples * sizeof(float);
Serial.write((uint8_t*)samples, bytesToSend);
// Wait for the receiver to acknowledge receipt of the chunk
while (!Serial.available());
char ack = Serial.read();
if (ack != 'A') {
// Handle error maybe
Serial.println("Error in data transmission.");
}
}
Python Kivy code:
Rich (BB code):
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.clock import Clock
from kivy.garden.graph import Graph, MeshLinePlot
import serial
import threading
import struct
class TeensyApp(App):
def build(self):
self.serial_port = serial.Serial('COM5', 115200, timeout=1)
self.serial_port.flush()
layout = BoxLayout(orientation='vertical')
self.response_text = TextInput(size_hint=(1, 0.4), readonly=True)
layout.add_widget(self.response_text)
button_layout = BoxLayout(size_hint=(1, 0.2))
self.t_button = Button(text='Send t')
self.t_button.bind(on_press=self.send_t)
button_layout.add_widget(self.t_button)
self.c1_button = Button(text='Send C1')
self.c1_button.bind(on_press=self.send_c1)
button_layout.add_widget(self.c1_button)
self.c2_button = Button(text='Send C2')
self.c2_button.bind(on_press=self.send_c2)
button_layout.add_widget(self.c2_button)
self.r_button = Button(text='Send r')
self.r_button.bind(on_press=self.send_r)
button_layout.add_widget(self.r_button)
layout.add_widget(button_layout)
self.graph = Graph(
xlabel='Time', ylabel='Amplitude',
x_ticks_minor=5, x_ticks_major=25,
y_ticks_minor=0.5, y_ticks_major=1,
y_grid_label=True, x_grid_label=True, padding=5,
x_grid=True, y_grid=True, xmin=0, xmax=30000, ymin=-2, ymax=2
)
self.plot = MeshLinePlot(color=[1, 0, 0, 1])
self.graph.add_plot(self.plot)
layout.add_widget(self.graph)
# Start a thread to read data from the serial port
self.data_buffer = bytearray()
self.lock = threading.Lock()
self.receiving_data = False
self.data_length = 120000 # 30,000 floats * 4 bytes per float
self.current_state = "IDLE"
threading.Thread(target=self.read_from_port, daemon=True).start()
return layout
def send_t(self, instance):
with self.lock:
self.serial_port.write(b't')
self.current_state = "WAIT_FOR_RESPONSE"
Clock.schedule_once(lambda dt: self.update_textbox('Sent t command'), 0)
def send_c1(self, instance):
with self.lock:
self.serial_port.write(b'C1')
self.current_state = "WAIT_FOR_RESPONSE"
Clock.schedule_once(lambda dt: self.update_textbox('Sent C1 command'), 0)
def send_c2(self, instance):
with self.lock:
self.serial_port.write(b'C2')
self.current_state = "WAIT_FOR_RESPONSE"
Clock.schedule_once(lambda dt: self.update_textbox('Sent C2 command'), 0)
def send_r(self, instance):
with self.lock:
self.serial_port.write(b'r')
self.current_state = "WAIT_FOR_D"
Clock.schedule_once(lambda dt: self.update_textbox('Sent r command'), 0)
def read_from_port(self):
while True:
try:
with self.lock:
if self.serial_port.in_waiting > 0:
if self.current_state == "WAIT_FOR_RESPONSE":
line = self.serial_port.readline().decode('utf-8', errors='ignore').strip()
Clock.schedule_once(lambda dt: self.update_textbox(f'Received response: {line}'), 0)
self.current_state = "IDLE"
elif self.current_state == "WAIT_FOR_D":
line = self.serial_port.readline().decode('utf-8', errors='ignore').strip()
if line == 'D':
Clock.schedule_once(lambda dt: self.update_textbox('Data transfer started (D)'), 0)
self.current_state = "RECEIVE_DATA"
self.data_buffer = bytearray()
else:
Clock.schedule_once(lambda dt: self.update_textbox(f'Received line: {line}'), 0)
elif self.current_state == "RECEIVE_DATA":
if len(self.data_buffer) < self.data_length:
self.data_buffer.extend(self.serial_port.read(self.serial_port.in_waiting))
if len(self.data_buffer) >= self.data_length:
self.serial_port.write(b'A') # Acknowledge receipt of data
self.current_state = "WAIT_FOR_E"
else:
self.current_state = "WAIT_FOR_E"
elif self.current_state == "WAIT_FOR_E":
line = self.serial_port.readline().decode('utf-8', errors='ignore').strip()
if line == 'E':
Clock.schedule_once(lambda dt: self.update_textbox('Data transfer ended (E)'), 0)
self.process_data()
self.current_state = "IDLE"
except Exception as e:
Clock.schedule_once(lambda dt: self.update_textbox(f'Error: {e}'), 0)
def update_textbox(self, line):
self.response_text.text += f"{line}\n"
def process_data(self):
total_floats = len(self.data_buffer) // 4
if total_floats == 30000:
unpacked_data = struct.unpack('f' * total_floats, self.data_buffer)
# Plotting the received 30,000 samples
self.plot.points = [(i, unpacked_data) for i in range(len(unpacked_data))]
Clock.schedule_once(lambda dt: self.update_textbox(f'Received {len(unpacked_data)} samples'), 0)
else:
Clock.schedule_once(lambda dt: self.update_textbox(f'Error: Received {total_floats} floats, expected 30000'), 0)
if __name__ == '__main__':
TeensyApp().run()
Thanks for any advice!
Michal