I have a circuit with a one-wire sensor and one button. The aim of the program is to collect the temperature of an external camera rig in real-time (and print it over serial) and when the button is pushed it should send the word "capture" over serial.
I have a VB.net program that listens to the COM port and will collect the serial data and do stuff with it. The problem is the VB program keeps freezing but pressing the reset button on the Arduino allows it to carry on! I think it is due to the Arduino. Code attached below, can anyone see a problem with this or suggest a better way to achieve my goal?
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 6
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
float Celcius=0;
float lastTemp = 0;
int tempOffset = 3;
const int BUTTON_PIN = 2; // Pin number of the rig switch
const int FAIL_PIN = 3; // Pin number of the PLC output for a failed image
const int PASS_PIN = 4; // Pin number of the PLC output for a passed image
const int LED_PIN = 5; // Pin number of the status LED
String data = ""; // For incoming serial string data
volatile byte state = LOW;
void setup()
{
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT);
pinMode(BUTTON_PIN,INPUT_PULLUP);
pinMode(ONE_WIRE_BUS,INPUT_PULLUP);
Serial.begin(9600);
while (!Serial) ; //Wait for Arduino serial monitor to open
sensors.requestTemperatures();
Celcius=sensors.getTempCByIndex(0);
lastTemp = round(Celcius); //Store the current temp
Serial.println(" Arduino_Interface_Connected"); //DO NOT DELETE WHITE SPACE FROM FRONT OF THIS STRING!!!!
Serial.print(" TEMP=");
Serial.println(round(Celcius)); //Write the temp at start as the program needs it at startup
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonPressed, FALLING ); //Attach interupt to BUTTON_PIN so it interupts the TEMP reading loop
}
void loop() {
GetSerialData();
GetTemp();
FlashLED();
}
void buttonPressed() {
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
// If interrupts come faster than 500ms, assume it's a bounce and ignore
if (interrupt_time - last_interrupt_time > 500)
{
state = HIGH;
Serial.println ("capture");
}
last_interrupt_time = interrupt_time;
}
void(* resetFunc) (void) = 0;
void GetTemp() {
sensors.requestTemperatures();
Celcius=sensors.getTempCByIndex(0);
if (round(Celcius) == -127) {
//If the temp is -127 (known error) then don't write it out
}
//If the temp is greater or less than the offset then write the value to the serial port
if (round(Celcius) == lastTemp + tempOffset || round(Celcius) == lastTemp - tempOffset) {
Serial.print(" TEMP=");
Serial.println(round(Celcius));
delay(1000);
lastTemp = round(Celcius);
}
}
void GetSerialData() {
if (Serial.available()) {
// read the incoming string:
String data;
data = Serial.readString();
data.trim();
if (data == "thermal?") {
Serial.println("Thermal_Interface_Connected");
return;
}
if (data == "temp?") {
Serial.print(" TEMP=");
Serial.println(round(Celcius));
return;
}
if (data == "on") {
digitalWrite(LED_PIN, HIGH);
}
if (data == "off") {
digitalWrite(LED_PIN, LOW);
}
if (data == "reset") {
resetFunc();
}
Serial.println(data);
}
}
void FlashLED() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on
delay(500); // wait for half a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off
delay(500); // wait for half a second
}
I have a circuit with a one-wire sensor and one button. The aim of the program is to collect the temperature of an external camera rig in real-time (and print it over serial) and when the button is pushed it should send the word "capture" over serial.
I have a VB.net program that listens to the COM port and will collect the serial data and do stuff with it. The problem is the VB program keeps freezing but pressing the reset button on the Arduino allows it to carry on! I think it is due to the Arduino. Code attached below, can anyone see a problem with this or suggest a better way to achieve my goal?
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 6
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
float Celcius=0;
float lastTemp = 0;
int tempOffset = 3;
const int BUTTON_PIN = 2; // Pin number of the rig switch
const int FAIL_PIN = 3; // Pin number of the PLC output for a failed image
const int PASS_PIN = 4; // Pin number of the PLC output for a passed image
const int LED_PIN = 5; // Pin number of the status LED
String data = ""; // For incoming serial string data
volatile byte state = LOW;
void setup()
{
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT);
pinMode(BUTTON_PIN,INPUT_PULLUP);
pinMode(ONE_WIRE_BUS,INPUT_PULLUP);
Serial.begin(9600);
while (!Serial) ; //Wait for Arduino serial monitor to open
sensors.requestTemperatures();
Celcius=sensors.getTempCByIndex(0);
lastTemp = round(Celcius); //Store the current temp
Serial.println(" Arduino_Interface_Connected"); //DO NOT DELETE WHITE SPACE FROM FRONT OF THIS STRING!!!!
Serial.print(" TEMP=");
Serial.println(round(Celcius)); //Write the temp at start as the program needs it at startup
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonPressed, FALLING ); //Attach interupt to BUTTON_PIN so it interupts the TEMP reading loop
}
void loop() {
GetSerialData();
GetTemp();
FlashLED();
}
void buttonPressed() {
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
// If interrupts come faster than 500ms, assume it's a bounce and ignore
if (interrupt_time - last_interrupt_time > 500)
{
state = HIGH;
Serial.println ("capture");
}
last_interrupt_time = interrupt_time;
}
void(* resetFunc) (void) = 0;
void GetTemp() {
sensors.requestTemperatures();
Celcius=sensors.getTempCByIndex(0);
if (round(Celcius) == -127) {
//If the temp is -127 (known error) then don't write it out
}
//If the temp is greater or less than the offset then write the value to the serial port
if (round(Celcius) == lastTemp + tempOffset || round(Celcius) == lastTemp - tempOffset) {
Serial.print(" TEMP=");
Serial.println(round(Celcius));
delay(1000);
lastTemp = round(Celcius);
}
}
void GetSerialData() {
if (Serial.available()) {
// read the incoming string:
String data;
data = Serial.readString();
data.trim();
if (data == "thermal?") {
Serial.println("Thermal_Interface_Connected");
return;
}
if (data == "temp?") {
Serial.print(" TEMP=");
Serial.println(round(Celcius));
return;
}
if (data == "on") {
digitalWrite(LED_PIN, HIGH);
}
if (data == "off") {
digitalWrite(LED_PIN, LOW);
}
if (data == "reset") {
resetFunc();
}
Serial.println(data);
}
}
void FlashLED() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on
delay(500); // wait for half a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off
delay(500); // wait for half a second
}
I cannot post my VB program as it is huge and I agree there is no need.
I'm more interested in how other people have done this and if there is anything I need to look out for which "could" be causing the program to freeze. I might be barking up the wrong tree but I have written loads of other programs which don't use Ardunio and serial ports with no issue.
Can anyone provide "working" code that will allow me to write a string to the Arduino and have it send a string back? It just doesn't seem to work yet I am getting status from buttons and temp sensor from Ardunio no problem.
In case anyone is interested I found the issue and it was in the .net program!
There is a windows event, namely "SystemEvents.UserPreferenceChanged", this event fires when any user preferences such as font, screen saver etc are changed. This event was "firing" and causing handlers in my program to freeze waiting for the event to respond.
More info on "UserPreferenceChanged" can be found at:
The SystemEvents class is the troublemaker here, it makes an attempt to raise it events on the UI thread of a program. Which is very important, UI isn't thread-safe. Trouble is, you've got two threads that created UI. SystemEvents is incapable of guessing which one is right, it only has 50% odds at it so is doomed to get it wrong. If it initially guessed wrong at which thread in your program is the UI thread, and that thread exited, then it will be 100% wrong.
This makes creating UI on a worker thread exceedingly hazardous of course. It is technically possible, you'll however have to avoid using several controls from the toolbox. They don't handle the UserPreferenceChanged event well when it is raised on the wrong thread. The ones that definitely cause deadlock are DataGridView, NumericUpDown, DomainUpDown, ToolStrip+MenuStrip and the ToolStripItem derived classes. The ones that are iffy (can't analyze the code deep enough) are RichTextBox and ProgressBar. Looks like I ought to put ProgressBar in the first set, judging from your callstacks.
The true cure is to not create UI on a worker thread. It is never necessary, the UI thread of your program is already capable of handling any number of windows.
I am using handlers to detect files changing in a folder ... it appears that this one was being picked up (even though it was not subscribed too) and causing the issues.
I am using handlers to detect files changing in a folder ... it appears that this one was being picked up (even though it was not subscribed too) and causing the issues.
Not sure what you mean by subscribed to.
If the event handler is there, it is going to run if the event occurs.
Perhaps you mean you did not realize that the event handler did what you were not expecting.
I was adding handlers as below but they were being added to form2, not form1, for some reason when "UserPreferenceChanged" fired it was hanging to UI thread.
Private Sub Load()
watchfolder.Path = My.Settings.ImageSaveFolder
watchfolder.EnableRaisingEvents = True
AddHandler watchfolder.Created, AddressOf watchfolderchanged
AddHandler watchfolder.Deleted, AddressOf watchfolderchanged
End Sub
Sub watchfolderchanged(ByVal source As Object, ByVal e As System.IO.FileSystemEventArgs)
Do Stuff!
End Sub
I added the following code to form1 and it now doesn't hang, if you know a better way I'm all ears as I'm new to handlers and only know what Google has told me.
Protected Overrides Sub OnHandleCreated(ByVal e As System.EventArgs)
MyBase.OnHandleCreated(e)
AddHandler SystemEvents.UserPreferenceChanged, New UserPreferenceChangedEventHandler(AddressOf Me.UserPreferenceChangedHandler)
End Sub
Protected Overrides Sub OnHandleDestroyed(ByVal e As System.EventArgs)
RemoveHandler SystemEvents.UserPreferenceChanged, New UserPreferenceChangedEventHandler(AddressOf Me.UserPreferenceChangedHandler)
MyBase.OnHandleDestroyed(e)
End Sub
Private Sub UserPreferenceChangedHandler(ByVal sender As Object, ByVal e As UserPreferenceChangedEventArgs)
Debug.Print("UserPreferenceChangedHandler Fired!")
End Sub