I am working on a home light project and have it working, in a simple fashion so one switch controls multiple lights.
I have introduced another array that checks previous state, if there is no change then it doesn't drop into the routine that turns the light on or off. I have two reasons for doing this, first at the moment the routine is constantly pulling the pin high. The second and more import reason, I have created another project that controls the lights via a webpage. I would like to combine the switches and web control into one project. If I have the switches saying off constantly and the web saying on it will cause me problems.
With the previous state array introduced switches control the wrong light or not at all and it seems to be in a random state.
const int zone2[] = {8,13,0};
const int zone3[] = {11,0};
const int zone4[] = {7,0};
const int zone5[] = {9,0};
const int zone6[] = {12,0};
const int * zones[]={zone2,zone3,zone4,zone5,zone6};
int buttonState[] = {0,0}; // variable for reading the pushbutton status
int previousState[]={0,0,0,0,0}; // array for holding the previous state
void setup()
{
//initialize the output pins that will control lights
pinMode(11, OUTPUT);
pinMode(12, OUTPUT);
pinMode(13,OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9,OUTPUT);
// initialize the pushbutton pin as an input:
//set all light switches to the same block ie pins 30 - 36
byte i;
//this loop sets all the pins as inputs
for (i=30;i< 36;i++) {
pinMode(i, INPUT);
digitalWrite(i,HIGH); // this makes it connect to the internal resistor
}
}
void loop()
{
// read the state of the pushbutton value:
byte myInput =2; //?? is this needed?? don't thinks so
// check if the pushbutton is pressed.
// if it is, the buttonState is HIGH:
int ArrayCount;
int arrayPosition;
for (int z = 0; z < 5; ++z)
{
buttonState[z] = digitalRead(z+30);
for (arrayPosition = 0;zones[z][arrayPosition] ; arrayPosition++)
{
if ((buttonState[z] == HIGH) && (previousState[z] == 0 ))
// turn LED on:
digitalWrite(zones[z][arrayPosition],HIGH);
previousState[z] = 1;//I have added this line to handle previous state
}
else if ((buttonState[z] == LOW) && (previousState[z] == 1)) {
// turn LED off;
digitalWrite(zones[z][arrayPosition],LOW);
previousState[z] = 0;//I have added this line to handle previous state
}
}
}
}
Related
I'm a first semester software student and for a course I am exploring the world of C++ and Arduino (UNO in my case).
My assignment is to make a timer counting down from 10 minutes (shown on a 4 digit display).
The thing I struggled with is how to program a pause functionality on the same button (btnOne) that also starts the timer. When I finally discovered how, I noticed something buggy: the delay() might be causing some trouble. See my code for implementation.
When clicking on the start/pause button the button doesn't immediately register it. Possibly because the delay might be blocking the communication of user input to the button.
The delay (of 1 second) is used to limit the execution of the if-statement in timer() to once per second; like a timer.
My question is: how can I improve my code so that the clicks on btnOne are immediately registered?
Thanks so much in advance.
const int btnOne = 8;
const int btnTwo = 9;
int btnOn = LOW;
int btnOff = HIGH;
const int clockPin = 10;
const int dataPin = 11;
const int buzzer = 3;
int btnOneState = 0;
int btnTwoState = 0;
long time = 600000;
long timeValueState = time;
int timerDisplay = 1000;
bool pause = true;
void setup()
{
Serial.begin(9600);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(btnOne, INPUT_PULLUP);
pinMode(btnTwo, INPUT_PULLUP);
pinMode(buzzer, OUTPUT);
}
void timer() {
// Count down from 600000 to 0
if (timeValueState >= 600 ) {
timeValueState = timeValueState - 600;
Display.show(--timerDisplay);
// When reached last count, display is set to 0 and buzzer turns on for 5 seconds
if (timeValueState == 600) {
Display.show(timerDisplay == 0);
tone(buzzer, 200);
delay(5000);
noTone(buzzer);
}
Serial.println("time:");
Serial.println(timeValueState);
delay(1000);
}
}
void loop()
{
btnOneState = digitalRead(btnOne);
btnTwoState = digitalRead(btnTwo);
// If button is on, change state of pause (true, false)
if (btnOneState == btnOn) {
Serial.println("btnOne pressed");
pause = !pause;
}
// When button is off and pause is not true, activate the timer
else if (btnOneState == btnOff && pause == false) {
Serial.println("btnOne NOT pressed");
timer();
}
// Reset time and timerDisplay back to 10 minutes.
if (btnTwoState == btnOn) {
Serial.println("btnTwo pressed");
timeValueState = time;
Display.show(timerDisplay = 1000);
}
}
I won't give you code so you learn more.
You can run a loop that checks the return value of millis vs a timestamp stored in a variable, when you started. Every 1000 milliseconds you update your display.
That way you don't block your code for 1 second.
This should serve as a good starting point for further research.
I have implemented a Xdnd drop support implementation in VTK some time ago. It was working great except with Thunar file manager. All other file managers were working fine at the time. We dismissed this limitation a Thunar bug at the time.
The feature I implemented was very simple:
Set the window of the application to be XdndAware
Receive the position message and respond that we are ready to receive
Receive the drop mesage and request a selection
Receive the selection notify and recover the URI
Convert the URI into something we can work with
Nothing fancy, I did not even touch the list type.
Fast forward a few years and now dolphin users cannot drop files correctly into our application. The URI is always the first file dropped since dolphin was started. Restarting our application has no effect. No bug at all with pcmanfm.
This is not a dolphin bug and files can be dropped on blender or firefox from dolphin without issues.
So there must be a bug in our implementation, but I've been staring at the code for some time and everything I tried had no effect, except for breaking Xdnd support completely.
Here are the interesting part of the implementation:
//------------------------------------------------------------------------------
vtkXRenderWindowInteractor::vtkXRenderWindowInteractor()
{
this->Internal = new vtkXRenderWindowInteractorInternals;
this->DisplayId = nullptr;
this->WindowId = 0;
this->KillAtom = 0;
this->XdndSource = 0;
this->XdndPositionAtom = 0;
this->XdndDropAtom = 0;
this->XdndActionCopyAtom = 0;
this->XdndStatusAtom = 0;
this->XdndFinishedAtom = 0;
}
[...]
//------------------------------------------------------------------------------
void vtkXRenderWindowInteractor::Enable()
{
// avoid cycles of calling Initialize() and Enable()
if (this->Enabled)
{
return;
}
// Add the event handler to the system.
// If we change the types of events processed by this handler, then
// we need to change the Disable() routine to match. In order for Disable()
// to work properly, both the callback function AND the client data
// passed to XtAddEventHandler and XtRemoveEventHandler must MATCH
// PERFECTLY
XSelectInput(this->DisplayId, this->WindowId,
KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask |
StructureNotifyMask | EnterWindowMask | LeaveWindowMask | PointerMotionHintMask |
PointerMotionMask);
// Setup for capturing the window deletion
this->KillAtom = XInternAtom(this->DisplayId, "WM_DELETE_WINDOW", False);
XSetWMProtocols(this->DisplayId, this->WindowId, &this->KillAtom, 1);
// Enable drag and drop
Atom xdndAwareAtom = XInternAtom(this->DisplayId, "XdndAware", False);
char xdndVersion = 5;
XChangeProperty(this->DisplayId, this->WindowId, xdndAwareAtom, XA_ATOM, 32, PropModeReplace,
(unsigned char*)&xdndVersion, 1);
this->XdndPositionAtom = XInternAtom(this->DisplayId, "XdndPosition", False);
this->XdndDropAtom = XInternAtom(this->DisplayId, "XdndDrop", False);
this->XdndActionCopyAtom = XInternAtom(this->DisplayId, "XdndActionCopy", False);
this->XdndStatusAtom = XInternAtom(this->DisplayId, "XdndStatus", False);
this->XdndFinishedAtom = XInternAtom(this->DisplayId, "XdndFinished", False);
this->Enabled = 1;
this->Modified();
}
[...]
//------------------------------------------------------------------------------
void vtkXRenderWindowInteractor::DispatchEvent(XEvent* event)
{
int xp, yp;
switch (event->type)
{
[...]
// Selection request for drag and drop has been delivered
case SelectionNotify:
{
// Sanity checks
if (!event->xselection.property || !this->XdndSource)
{
return;
}
// Recover the dropped file
char* data = nullptr;
Atom actualType;
int actualFormat;
unsigned long itemCount, bytesAfter;
XGetWindowProperty(this->DisplayId, event->xselection.requestor, event->xselection.property,
0, LONG_MAX, False, event->xselection.target, &actualType, &actualFormat, &itemCount,
&bytesAfter, (unsigned char**)&data);
// Conversion checks
if ((event->xselection.target != AnyPropertyType && actualType != event->xselection.target) ||
itemCount == 0)
{
return;
}
// Recover filepaths from uris and invoke DropFilesEvent
std::stringstream uris(data);
std::string uri, protocol, hostname, filePath;
std::string unused0, unused1, unused2, unused3;
vtkNew<vtkStringArray> filePaths;
while (std::getline(uris, uri, '\n'))
{
if (vtksys::SystemTools::ParseURL(
uri, protocol, unused0, unused1, hostname, unused3, filePath, true))
{
if (protocol == "file" && (hostname.empty() || hostname == "localhost"))
{
// The uris can be crlf delimited, remove ending \r if any
if (filePath.back() == '\r')
{
filePath.pop_back();
}
// The extracted filepath miss the first slash
filePath.insert(0, "/");
filePaths->InsertNextValue(filePath);
}
}
}
this->InvokeEvent(vtkCommand::DropFilesEvent, filePaths);
XFree(data);
// Inform the source the the drag and drop operation was sucessfull
XEvent reply;
memset(&reply, 0, sizeof(reply));
reply.type = ClientMessage;
reply.xclient.window = event->xclient.data.l[0];
reply.xclient.message_type = this->XdndFinishedAtom;
reply.xclient.format = 32;
reply.xclient.data.l[0] = this->WindowId;
reply.xclient.data.l[1] = itemCount;
reply.xclient.data.l[2] = this->XdndActionCopyAtom;
XSendEvent(this->DisplayId, this->XdndSource, False, NoEventMask, &reply);
XFlush(this->DisplayId);
this->XdndSource = 0;
}
break;
case ClientMessage:
{
if (event->xclient.message_type == this->XdndPositionAtom)
{
// Drag and drop event inside the window
// Recover the position
int xWindow, yWindow;
int xRoot = event->xclient.data.l[2] >> 16;
int yRoot = event->xclient.data.l[2] & 0xffff;
Window root = DefaultRootWindow(this->DisplayId);
Window child;
XTranslateCoordinates(
this->DisplayId, root, this->WindowId, xRoot, yRoot, &xWindow, &yWindow, &child);
// Convert it to VTK compatible location
double location[2];
location[0] = static_cast<double>(xWindow);
location[1] = static_cast<double>(this->Size[1] - yWindow - 1);
this->InvokeEvent(vtkCommand::UpdateDropLocationEvent, location);
// Reply that we are ready to copy the dragged data
XEvent reply;
memset(&reply, 0, sizeof(reply));
reply.type = ClientMessage;
reply.xclient.window = event->xclient.data.l[0];
reply.xclient.message_type = this->XdndStatusAtom;
reply.xclient.format = 32;
reply.xclient.data.l[0] = this->WindowId;
reply.xclient.data.l[1] = 1; // Always accept the dnd with no rectangle
reply.xclient.data.l[2] = 0; // Specify an empty rectangle
reply.xclient.data.l[3] = 0;
reply.xclient.data.l[4] = this->XdndActionCopyAtom;
XSendEvent(this->DisplayId, event->xclient.data.l[0], False, NoEventMask, &reply);
XFlush(this->DisplayId);
}
else if (event->xclient.message_type == this->XdndDropAtom)
{
// Item dropped in the window
// Store the source of the drag and drop
this->XdndSource = event->xclient.data.l[0];
// Ask for a conversion of the selection. This will trigger a SelectioNotify event later.
Atom xdndSelectionAtom = XInternAtom(this->DisplayId, "XdndSelection", False);
XConvertSelection(this->DisplayId, xdndSelectionAtom,
XInternAtom(this->DisplayId, "UTF8_STRING", False), xdndSelectionAtom, this->WindowId,
CurrentTime);
}
else if (static_cast<Atom>(event->xclient.data.l[0]) == this->KillAtom)
{
this->ExitCallback();
}
}
break;
}
}
[...]
And header:
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderingUIModule.h" // For export macro
#include <X11/Xlib.h> // Needed for X types in the public interface
class vtkCallbackCommand;
class vtkXRenderWindowInteractorInternals;
class VTKRENDERINGUI_EXPORT vtkXRenderWindowInteractor : public vtkRenderWindowInteractor
{
public:
static vtkXRenderWindowInteractor* New();
vtkTypeMacro(vtkXRenderWindowInteractor, vtkRenderWindowInteractor);
void PrintSelf(ostream& os, vtkIndent indent) override;
/**
* Initializes the event handlers without an XtAppContext. This is
* good for when you don't have a user interface, but you still
* want to have mouse interaction.
*/
void Initialize() override;
/**
* Break the event loop on 'q','e' keypress. Want more ???
*/
void TerminateApp() override;
/**
* Run the event loop and return. This is provided so that you can
* implement your own event loop but yet use the vtk event handling as
* well.
*/
void ProcessEvents() override;
///#{
/**
* Enable/Disable interactions. By default interactors are enabled when
* initialized. Initialize() must be called prior to enabling/disabling
* interaction. These methods are used when a window/widget is being
* shared by multiple renderers and interactors. This allows a "modal"
* display where one interactor is active when its data is to be displayed
* and all other interactors associated with the widget are disabled
* when their data is not displayed.
*/
void Enable() override;
void Disable() override;
///#}
/**
* Update the Size data member and set the associated RenderWindow's
* size.
*/
void UpdateSize(int, int) override;
/**
* Re-defines virtual function to get mouse position by querying X-server.
*/
void GetMousePosition(int* x, int* y) override;
void DispatchEvent(XEvent*);
protected:
vtkXRenderWindowInteractor();
~vtkXRenderWindowInteractor() override;
/**
* Update the Size data member and set the associated RenderWindow's
* size but do not resize the XWindow.
*/
void UpdateSizeNoXResize(int, int);
// Using static here to avoid destroying context when many apps are open:
static int NumAppInitialized;
Display* DisplayId;
Window WindowId;
Atom KillAtom;
int PositionBeforeStereo[2];
vtkXRenderWindowInteractorInternals* Internal;
// Drag and drop related
Window XdndSource;
Atom XdndPositionAtom;
Atom XdndDropAtom;
Atom XdndActionCopyAtom;
Atom XdndStatusAtom;
Atom XdndFinishedAtom;
///#{
/**
* X-specific internal timer methods. See the superclass for detailed
* documentation.
*/
int InternalCreateTimer(int timerId, int timerType, unsigned long duration) override;
int InternalDestroyTimer(int platformTimerId) override;
///#}
void FireTimers();
/**
* This will start up the X event loop and never return. If you
* call this method it will loop processing X events until the
* application is exited.
*/
void StartEventLoop() override;
private:
vtkXRenderWindowInteractor(const vtkXRenderWindowInteractor&) = delete;
void operator=(const vtkXRenderWindowInteractor&) = delete;
};
#endif
The complete file can be seen here:
https://gitlab.kitware.com/vtk/vtk/-/blob/master/Rendering/UI/vtkXRenderWindowInteractor.cxx
You can follow my train of thoughts and debugs here:
https://gitlab.kitware.com/f3d/f3d/-/issues/228
To test this code, a simple way is to use F3D has it is using the dropped file, but a simple VTK application should work as well:
https://gitlab.kitware.com/f3d/f3d
Current Dolphin issue
From some testing, the issue is with the preparation and sending of the XdndFinished ClientMessage back to the drag and drop source when handling the SelectionNotify event.
Instead of:
reply.xclient.window = event->xclient.data.l[0];
the line should be:
reply.xclient.window = this->XdndSource;
This aligns the XClientMessageEvent window member with the target window argument to XSendEvent. This is probably a simple copy-paste error, as xclient isn't valid for the SelectionNotify event type. Most likely the actual value of window was not previously being checked, but that has been changed recently, hence the error.
The spec covers this quite well, and also raises a couple of other things to consider:
For data.l[1]: "Bit 0 is set if the current target accepted the drop and successfully performed the accepted drop action. (new in version 5)", so using itemCount as a value will technically be incorrect whenever the count is even
If the handling of XdndPosition doesn't need to actually track where the current position is (i.e. if you are just using the entire window as a drop target), you may be able to get away with sending the XdndStatus in response to the XdndEnter message
Previous Thunar issue
Looking into this further, I did some troubleshooting regarding the previous issue with Thunar, and it boils down to the code handling XdndDrop assuming that the format of the incoming data can be converted to UTF8_STRING. This diff for GLFW handles almost the exact same issue.
If, when handling the XdndEnter message, you inspect the values of xclient.data.l[2] through xclient.data.l[4] you can see that Dolphin reports supporting the following formats:
text/uri-list
text/x-moz-url
text/plain
whereas Thunar only supports the following:
text/uri-list
The simplest solution is to:
Keep track of a supported format when handling XdndEnter
Provide this format to XConvertSelection when handling XdndDrop (instead of UTF8_STRING)
Handle the format appropriately when handling the SelectionNotify event
To be more complete, if bit 0 of xclient.data.l[1] is set on the XdndEnter message, you should get the XdndTypeList property of the drag and drop source window and based the format selection on that, instead of the formats contained in the message itself.
I made a code where, for every time I press the button, it plays the next note in the song. But the problem is, for some reason it keeps skipping the same notes in the array.
For example (my code):
int mariomelody[] = {
NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_G5, NOTE_G4,
//THERE ARE TWO 'NOTE_E5's BECAUSE THE THIRD NOTE DOESNT PLAY
NOTE_C5, NOTE_G4, NOTE_E4, NOTE_A4, NOTE_B5, NOTE_AS4, NOTE_A4,
NOTE_G4, NOTE_E5, NOTE_G5, NOTE_A5, NOTE_F5, NOTE_G5, NOTE_E5,
NOTE_C5, NOTE_D5, NOTE_B4,
//buttonPin sits at DIG. 12
int buttonPin = 12;
void setup()
{
// put your setup code here, to run once:
pinMode(buttonPin, INPUT);
}
void loop()
{
// put your main code here, to run repeatedly:
for (int i = 0; i < sizeof(mariomelody) / sizeof(mariomelody[0]); i++)
{
while (digitalRead(buttonPin) == HIGH)
{
tone(8, mariomelody[i], 20);
}
while (digitalRead(buttonPin) == LOW)
{
}
}
Everybody knows the mario melody right. The first three notes are the same at different speeds (4 is half the speed of 8); E(4) E(4) E(8). For some reason it just skips the third NOTE_E5 so I just put another NOTE_E5 there and now it works "just fine".
Does anybody know why it skips the notes? Is it my code?
I don't know the mario melody, but anyway I think your code is wrong:
Try this:
void loop()
{
for (int i = 0; i < sizeof(mariomelody) / sizeof(mariomelody[0]); i++)
{
while (digitalRead(buttonPin) == LOW)
{
// wait until button is pressed
}
tone(8, mariomelody[i], 20);
while (digitalRead(buttonPin) == HIGH)
{
// wait until button is released
}
}
}
I am using QT and QCustomPlot to create a real time plotting tool, and the data of the plot is read from the Arduino UNO board. My application succeeded in plotting while the data is a total mess. Here is my code below (Some code is from QCustomPlot website):
void Dialog::realtimeDataSlot()
{
bool currentPortNameChanged = false;
QString currentPortName;
if (currentPortName != portName) {
currentPortName = portName;
currentPortNameChanged = true;
}
QString currentRequest = request;
QSerialPort serial;
if (currentPortNameChanged) {
serial.close();
serial.setPortName(currentPortName);
if (!serial.open(QIODevice::ReadOnly)) {
return;
}
}
static QTime time(QTime::currentTime());
// calculate two new data points:
double key = time.elapsed()/1000.0;
static double lastPointKey = 0;
if (key-lastPointKey > 0.002) // at most add point every 2 ms
{
// add data to lines:
if(serial.waitForReadyRead(-1)){
data = serial.readAll();
QTextStream(stdout) << "HERE:" << data.toDouble() << endl;
customPlot->graph(0)->addData(key, data.toDouble());
customPlot->graph(0)->rescaleValueAxis(); //rescale value (vertical) axis to fit the current data:
lastPointKey = key;
customPlot->xAxis->setRange(key, 8, Qt::AlignRight);
customPlot->replot();
static double lastFpsKey;
static int frameCount;
++frameCount;
if (key-lastFpsKey > 2) // average fps over 2 seconds
{
lastFpsKey = key;
frameCount = 0;
}
}
}
// calculate frames per second:
if (currentPortName != portName) {
currentPortName = portName;
currentPortNameChanged = true;
} else {
currentPortNameChanged = false;
}
}
When I Tried to print out the data I read from the serial port, I found the following:
HERE:1
HERE:15
HERE:150
HERE:149
HERE:149
HERE:149
HERE:150
HERE:150
HERE:15
HERE:150
HERE:149
HERE:49
HERE:150
HERE:150
HERE:1
HERE:150
The values around 150 are normal while the value that are 0, 1 to others are not. Also it is not print out at a stable speed. I don't know what happened to this, and thanks to whoever may help, and I would appreciate it if there is any better ways to implement this.
The problem here is that it is not guaranteed that the serial transmission is received all at once. So it is better to let the serial to be processed somewhere else, for instance:
// in the class definition
QSerialPort serialPort;
private slots:
void handleReadyRead();
private:
QByteArray serialBuffer;
volatile double lastSerialValue;
// In the initialization part (not the realtimeDataSlot function)
lastSerialValue = qQNaN();
serialPort.setPortName(currentPortName);
connect(&serialPort, &QSerialPort::readyRead, this, &Dialog::handleReadyRead, Qt::UniqueConnection);
if (!serialPort.open(QIODevice::ReadOnly)) {
return;
}
serialBuffer.clear();
// Other functions:
void Dialog::realtimeDataSlot()
{
...
if (key-lastPointKey > 0.002) // at most add point every 2 ms
{
if (!qIsNaN(lastSerialData))
{
// use lastSerialValue as the data.toDouble() you had before, then, at the end
lastSerialValue = qQNaN();
}
...
}
void Dialog::handleReadyRead()
{
serialBuffer.append(serialPort.readAll());
int serPos;
while ((serPos = serialBuffer.indexOf('\n')) >= 0)
{
bool ok;
double tempValue = QString::fromLatin1(serialBuffer.left(serPos)).toDouble(&ok);
if (ok) lastSerialValue = tempValue;
serialBuffer = serialBuffer.mid(serPos+1);
}
}
Explanation: whenever you receive something from the arduino the bytes are appended to a buffer. Then the byte array is parsed looking for a terminator, and if found the byte array is split and analysed. When the other function needs the data, it simply pulls the most recent one saved in the variable.
NOTE 1: I saw that you used a binary transmission. The problem is that you do not have any way to determine where the data begins and end in this way. For instance, if you receive 0x01 0x02 0x03 0x04 and you know that there are 3 bytes, are they 01..03 or 02..04 or 03, 04 and a missing one or...? The version I implemented requires you to send data in string format with a new-line terminator (simplest version, you just have to write Serial.println(doubleValue); in the arduino code), but if you need the binary version I can give you some hints
NOTE 2: The code I wrote is NOT thread safe. It will work only if the realtimeDataSlot and the handleReadyRead are called in the same thread. Note that if they belong to the same object and are called through signals this is guaranteed.
Now, this should work. But I highly discourage you from doing this. I don't know who needs to call the realtimeDataSlot(), but I think that the most correct version is something like this:
// in the class definition
QSerialPort serialPort;
private slots:
void handleReadyRead();
void receivedData(double val);
private:
QByteArray serialBuffer;
signals:
void newData(double data);
// In the initialization part (not the realtimeDataSlot function)
serialPort.setPortName(currentPortName);
connect(&serialPort, &QSerialPort::readyRead, this, &Dialog::handleReadyRead, Qt::UniqueConnection);
connect(this, &Dialog::newData, this, &Dialog::receivedData, Qt::UniqueConnection);
if (!serialPort.open(QIODevice::ReadOnly)) {
return;
}
serialBuffer.clear();
// Other functions:
void Dialog::receivedData(double val)
{
double key = time.elapsed()/1000.0;
static double lastPointKey = 0;
if (key-lastPointKey > 0.002) // at most add point every 2 ms
{
QTextStream(stdout) << "HERE:" << data.toDouble() << endl;
customPlot->graph(0)->addData(key, data.toDouble());
customPlot->graph(0)->rescaleValueAxis();
...
}
}
void Dialog::handleReadyRead()
{
serialBuffer.append(serialPort.readAll());
int serPos;
while ((serPos = serialBuffer.indexOf('\n')) >= 0)
{
bool ok;
double tempValue = QString::fromLatin1(serialBuffer.left(serPos)).toDouble(&ok);
if (ok) emit newData(tempValue);
serialBuffer = serialBuffer.mid(serPos+1);
}
}
So keep the graph responsive to events (received a new data) instead of to a timer.
One more thing: I removed the port change on purpose. I suggest you to handle it in another way: put a button to start and stop the serial, and when the serial port is started prevent the user from changing the port name. This way the user will explicitely need to shut it down when he needs to change the port. If you want your version, however, don't include it in your code, but make a slot on its own to call whenever you need to change the port name:
void changeSerialPortName(QString newName)
{
if (newName != serialPort.portName()) {
if (serialPort.isOpen())
serialPort.close();
serialPort.setPortName(newName);
if (!serialPort.open(QIODevice::ReadOnly)) {
return;
}
}
}
Small description for you... Using arduino uno I am trying to make it so I press a button to switch on the entire program, not hold down press but press and let it release to make it on then do it again to make it off. While its on it needs to output data given from a LDR every 1 second.
Anyway, in the serial monitor (output of data) I want it to say "off" to start with then say "on" after the button is pressed. I have gotten this far.
My problem is when it is on, i cant figure out how to make it display the LDR light sense amount each second while also testing that if it goes over 500 for example, it should then stop and say alarm triggered. And be able to be switched off after also.
Here is my code:
// Devices attached
const int buttonPin = 2; // Pin that BUTTON uses
const int sensorPin = 0; // Pin that SENSOR uses
// List of dynamic variables
int pushCounter = 0; // counter for the number of button presses
int buttonState = 0; // current state of the button
int lastbuttonState = 0; // previous state of the button
void setup()
{
// Set the BUTTON as an INPUT
pinMode(buttonPin, INPUT);
// Create serial prompt
Serial.begin(9600);
}
void loop()
{
// read the pushbutton current state
buttonState = digitalRead(buttonPin);
// compare the buttonState to its previous state
if (buttonState != lastbuttonState)
{
// if the state has changed, increment the counter
if (buttonState == HIGH)
{
// if the current state is HIGH then the button
// wend from off to on:
pushCounter++;
int sensorValue = 0;
// Divides counter by 2, if remainder (not0), then the following
if (pushCounter % 2 == 0){
analogWrite(sensorPin, HIGH);
Serial.println("Power ON");
sensorValue = analogRead(sensorPin);
Serial.println(sensorValue);
delay(1000);
}
else
{
analogWrite(sensorPin, LOW);
Serial.println("OFF");
}
// Debouncing delay
delay(250);
}
// Save the current BUTTON state as the LAST state
lastbuttonState = buttonState;
}
}
Your requirement seems to be a question of task multithreading. It is not possible in bare arduinos. The arduino is single threaded.
analogRead() does not set the pin to be readable, but it returns the value in that pin.
Use analog A0 instead of digital pin 0
const int sensorPin = A0; // Pin that SENSOR uses
boolean isOn = false;
int sensorValue = 0, pinValue = 0;
void loop()
{
buttonState = digitalRead(buttonPin);
if(buttonState != lastbuttonState && buttonState == HIGH) {
pushCounter++;
lastbuttonState = buttonState;
}
if (pushCounter % 2 == 0){
Serial.println("Power ON");
isOn = true;
} else {
Serial.println("OFF");
isOn = false;
}
if(isOn) {
pinValue = analogRead(sensorPin);
Serial.println(pinValue);
if(pinValue > 500) { Serial.println("Alarm triggered"); }
delay(1000);
}
}