2

I'm currently attempting to replace delay() in my project so that i may perform other tasks while waiting on a particular event to fire.

The issue I'm having, is every example i've found using millis / elapsedmillis has everything inside the loop, where I'm trying to use it inside void buttons(). needless to say, either it's not going to work outside of loop, or I'm doing something ridiculous, and too tired, or too ignorant to see what i'm doing wrong..

The end goal is to have the button draw over the top of the existing one, wait 1000ms then draw the next button. (time interval will be changed later to sync with hardware spin down etc)

Here is the complete sketch, perhaps someone can show me a reliable way to replace delay in the buttons funciton:

#include <Adafruit_GFX.h>
#include <TouchScreen.h>
#include <stdint.h>
#include <SPI.h>
#include <SD.h>
#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;
#include <elapsedMillis.h>
elapsedMillis timer0;
elapsedMillis sinceTime1;
elapsedMillis sinceTime2;
elapsedMillis sinceTime3;
#define debug = false;
//#define debug = true;
#if defined(__SAM3X8E__)
#undef __FlashStringHelper::F(string_literal)
#define F(string_literal) string_literal
#endif
// most mcufriend shields use these pins and Portrait mode:
uint8_t YP = A1; // must be an analog pin, use "An" notation!
uint8_t XM = A2; // must be an analog pin, use "An" notation!
uint8_t YM = 6; // can be a digital pin
uint8_t XP = 7; // can be a digital pin
uint8_t Landscape = 0;
uint16_t TS_LEFT = 120;
uint16_t TS_RT = 940;
uint16_t TS_TOP = 940;
uint16_t TS_BOT = 180;
#define MINPRESSURE 10
#define MAXPRESSURE 1000
// For better pressure precision, we need to know the resistance
// between X+ and X- Use any multimeter to read it
// For the one we're using, its 250 ohms across the X plate
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
// Assign human-readable names to some common 16-bit color values:
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
int16_t BOXSIZE;
int16_t PENRADIUS = 3;
uint16_t oldcolor, currentcolor;
#define SD_SCK 13
#define SD_MISO 12
#define SD_MOSI 11
#define SD_CS 10
// In the SD card, place 24 bit color BMP files (be sure they are 24-bit!)
void setup() {
 // Init TouchScreen
 ts = TouchScreen(XP, YP, XM, YM, 300); //call the constructor AGAIN with new values.
 BOXSIZE = tft.width() / 6;
 tft.fillScreen(BLACK);
 // put your setup code here, to run once:
 Serial.begin(9600);
 tft.reset();
 uint16_t identifier = tft.readID();
if (identifier == 0x7789) {
 //Serial.println(F("Found ST7789V LCD driver"));
 TS_LEFT = 120; TS_RT = 940; TS_TOP = 940; TS_BOT = 180;
 Landscape = 1;
 } else {
 //Serial.print(F("Unknown LCD driver chip: "));
 //Serial.println(identifier, HEX);
 return;
 }
 //Serial.print(F("Found LCD driver chip ID: "));
 //Serial.println(identifier, HEX);
 tft.begin(identifier);
 //Serial.print(F("Initializing SD card..."));
 if (!SD.begin(SD_CS, SD_MOSI, SD_MISO, SD_SCK)) {
 Serial.println(F("failed!"));
 return;
 }
 //Serial.println(F("OK!"));
 tft.begin(identifier);
 tft.setRotation(3);
 BOXSIZE = tft.width() / 6;
 tft.fillScreen(BLACK);
 bmpDraw("splash.bmp", 80, 20);
 delay(5000);
 loadhome();
}
void loop() {
//DO STUFF HERE
//Testing Text stuff here
 tft.setTextColor(BLACK);
 tft.setTextSize(2); 
 tft.setCursor(15, 33);
 tft.print("123.456");
 tft.setTextColor(BLACK);
 tft.setCursor(115, 33);
 tft.print("543.210");
 tft.setTextColor(BLACK);
 tft.setCursor(215, 33);
 tft.print("789.654");
 buttons();
}
void loadhome(){
 tft.setRotation(3); 
 BOXSIZE = tft.width() / 6;
 tft.fillScreen(BLACK);
 bmpDraw("cell.bmp", 10, 25);
 bmpDraw("cell.bmp", 110, 25);
 bmpDraw("cell.bmp", 210, 25);
 bmpDraw("stop.bmp", 15, 175);
 bmpDraw("locked.bmp", 90, 175);
 bmpDraw("play.bmp", 165, 175);
 bmpDraw("home.bmp", 240, 175);
 bmpDraw("coolstp.bmp", 15, 105);
 bmpDraw("spinstp.bmp", 240, 105);
 tft.setTextColor(RED);
 tft.setTextSize(3); 
 tft.setCursor(50, 3);
 tft.print("X");
 tft.setTextColor(RED);
 tft.setCursor(150, 3);
 tft.print("Y");
 tft.setTextColor(RED);
 tft.setCursor(250, 3);
 tft.print("Z");
}
int homer = 0;
int play = 1;
int lock = 0;
int cool = 0;
int spin = 0;
const int interval = 1000;
unsigned long curMillis; //Current millis
void buttons(){
 //Touchscreen stuff
 TSPoint p = ts.getPoint();
 // if sharing pins, you'll need to fix the directions of the touchscreen pins
 pinMode(XM, OUTPUT);
 pinMode(YP, OUTPUT);
 p.x = map(p.x, TS_LEFT, TS_RT, 0, tft.width());
 p.y = map(p.y, TS_TOP, TS_BOT, 0, tft.height());
/*
//Use to map buttons
//Be sure to mark out button code before use!!
 if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
 Serial.print(F("p.y ="));
 Serial.print(p.y);
 Serial.println("");
 Serial.print(F("p.x ="));
 Serial.print(p.x);
 Serial.println("");
 }
*/ 
// Begin Buttons 
//Coolant Button
if (p.y > 110 && p.y < 170 && p.x > 250 && p.x < 300) { // if stop area is pressed
 if(cool == 0){
 bmpDraw("coolen.bmp", 15, 105);
 //Serial.println(F("Coolant Button Pressed!"));
 cool = 1;
 } else {
 bmpDraw("coolerr.bmp", 15, 105);
 sinceTime1 = 0;
 //delay(500); //replacing with millis()
 if (sinceTime1 >= 1000) {
 bmpDraw("coolstp.bmp", 15, 105);
 //Serial.println(F("Coolant Button Pressed!"));
 cool = 0;
 }
 }
}
//Stop Button
if (p.y > 185 && p.y < 235 && p.x > 250 && p.x < 300) { // if stop area is pressed
 //Serial.println(F("Stop Button Pressed!"));
 Serial.write(0x18);
 Serial.print("\n");
}
//Lock Button
if (p.y > 185 && p.y < 235 && p.x > 182 && p.x < 234) { // if stop area is pressed
 if(lock == 0){
 Serial.print("$X\n");
 bmpDraw("unlocked.bmp", 90, 175);
 //Serial.println(F("Lock/Unlock Button Pressed!"));
 lock = 1;
 } else {
 Serial.print("$X\n");
 bmpDraw("locked.bmp", 90, 175);
 //Serial.println(F("Lock/Unlock Button Pressed!"));
 lock = 0; 
 }
}
//Play-Pause Button
if (p.y > 185 && p.y < 235 && p.x > 110 && p.x < 160) { // if stop area is pressed
 if(play == 0){
 Serial.print("~\n");
 bmpDraw("play.bmp", 165, 175);
 //Serial.println(F("Play/Pause Button Pressed!"));
 play = 1;
 } else {
 Serial.print("!\n");
 bmpDraw("pause.bmp", 165, 175);
 //Serial.println(F("Play/Pause Button Pressed!"));
 play = 0; 
 }
}
//Home Button
if (p.y > 185 && p.y < 235 && p.x > 35 && p.x < 90) { // if stop area is pressed
 if(homer == 0){
 bmpDraw("homeg.bmp", 240, 175);
 //Serial.println(F("Home Button Pressed!"));
 homer = 1;
 } else {
 Serial.print("$H\n");
 bmpDraw("homey.bmp", 240, 175);
 sinceTime2 = 0;
 //delay(500); //replacing with millis()
 if (sinceTime2 >= 1000) {
 bmpDraw("homer.bmp", 240, 175);
 }
 //delay(500); //replacing with millis()
 sinceTime2 = 0;
 if (sinceTime2 >= 1000) {
 //delay(750);
 bmpDraw("homeg.bmp", 240, 175);
 //Serial.println(F("Home Button Pressed!"));
 }
 homer = 1;
 }
}
//Spindle Button
if (p.y > 110 && p.y < 170 && p.x > 35 && p.x < 90) { // if stop area is pressed
 if(spin == 0){
 bmpDraw("spinen.bmp", 240, 105);
 //Serial.println(F("Spindle Button Pressed!"));
 spin = 1;
 } else {
 bmpDraw("spinerr.bmp", 240, 105);
 sinceTime3 = 0;
 //delay(500); //replacing with millis()
 if (sinceTime3 >= 1000) {
 bmpDraw("spinstp.bmp", 240, 105);
 //Serial.println(F("Spindle Button Pressed!"));
 }
 spin = 0; 
 }
}
}
#define BUFFPIXEL 15
void bmpDraw(char *filename, int x, int y) {
 File bmpFile;
 int bmpWidth, bmpHeight; // W+H in pixels
 uint8_t bmpDepth; // Bit depth (currently must be 24)
 uint32_t bmpImageoffset; // Start of image data in file
 uint32_t rowSize; // Not always = bmpWidth; may have padding
 uint8_t sdbuffer[3*BUFFPIXEL]; // pixel in buffer (R+G+B per pixel)
 uint16_t lcdbuffer[BUFFPIXEL]; // pixel out buffer (16-bit per pixel)
 uint8_t buffidx = sizeof(sdbuffer); // Current position in sdbuffer
 boolean goodBmp = false; // Set to true on valid header parse
 boolean flip = true; // BMP is stored bottom-to-top
 int w, h, row, col;
 uint8_t r, g, b;
 uint32_t pos = 0, startTime = millis();
 uint8_t lcdidx = 0;
 boolean first = true;
 if((x >= tft.width()) || (y >= tft.height())) return;
 //Serial.println();
 //Serial.print(F("Loading image '"));
 //Serial.print(filename);
 //Serial.println('\'');
 // Open requested file on SD card
 if ((bmpFile = SD.open(filename)) == NULL) {
 Serial.println(F("File not found"));
 return;
 }
 // Parse BMP header
 if(read16(bmpFile) == 0x4D42) { // BMP signature
 read32(bmpFile);
 //Serial.println(F("File size: ")); 
 //Serial.println(read32(bmpFile));
 (void)read32(bmpFile); // Read & ignore creator bytes
 bmpImageoffset = read32(bmpFile); // Start of image data
 //Serial.print(F("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
 // Read DIB header
 read32(bmpFile);
 //Serial.print(F("Header size: ")); 
 //Serial.println(read32(bmpFile));
 bmpWidth = read32(bmpFile);
 bmpHeight = read32(bmpFile);
 if(read16(bmpFile) == 1) { // # planes -- must be '1'
 bmpDepth = read16(bmpFile); // bits per pixel
 //Serial.print(F("Bit Depth: ")); Serial.println(bmpDepth);
 if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed
 goodBmp = true; // Supported BMP format -- proceed!
 // Serial.print(F("Image size: "));
 // Serial.print(bmpWidth);
 // Serial.print('x');
 // Serial.println(bmpHeight);
 // BMP rows are padded (if needed) to 4-byte boundary
 rowSize = (bmpWidth * 3 + 3) & ~3;
 // If bmpHeight is negative, image is in top-down order.
 // This is not canon but has been observed in the wild.
 if(bmpHeight < 0) {
 bmpHeight = -bmpHeight;
 flip = false;
 }
 // Crop area to be loaded
 w = bmpWidth;
 h = bmpHeight;
 if((x+w-1) >= tft.width()) w = tft.width() - x;
 if((y+h-1) >= tft.height()) h = tft.height() - y;
 // Set TFT address window to clipped image bounds
 tft.setAddrWindow(x, y, x+w-1, y+h-1);
 for (row=0; row<h; row++) { // For each scanline...
 // Seek to start of scan line. It might seem labor-
 // intensive to be doing this on every line, but this
 // method covers a lot of gritty details like cropping
 // and scanline padding. Also, the seek only takes
 // place if the file position actually needs to change
 // (avoids a lot of cluster math in SD library).
 if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
 pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
 else // Bitmap is stored top-to-bottom
 pos = bmpImageoffset + row * rowSize;
 if(bmpFile.position() != pos) { // Need seek?
 bmpFile.seek(pos);
 buffidx = sizeof(sdbuffer); // Force buffer reload
 }
 for (col=0; col<w; col++) { // For each column...
 // Time to read more pixel data?
 if (buffidx >= sizeof(sdbuffer)) { // Indeed
 // Push LCD buffer to the display first
 if(lcdidx > 0) {
 tft.pushColors(lcdbuffer, lcdidx, first);
 lcdidx = 0;
 first = false;
 }
 bmpFile.read(sdbuffer, sizeof(sdbuffer));
 buffidx = 0; // Set index to beginning
 }
 // Convert pixel from BMP to TFT format
 b = sdbuffer[buffidx++];
 g = sdbuffer[buffidx++];
 r = sdbuffer[buffidx++];
 lcdbuffer[lcdidx++] = tft.color565(r,g,b);
 } // end pixel
 } // end scanline
 // Write any remaining data to LCD
 if(lcdidx > 0) {
 tft.pushColors(lcdbuffer, lcdidx, first);
 } 
 // Serial.print(F("Loaded in "));
 //Serial.print(millis() - startTime);
 //Serial.println(" ms");
 } // end goodBmp
 }
 }
 bmpFile.close();
 if(!goodBmp) Serial.println(F("BMP format not recognized."));
}
// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.
uint16_t read16(File f) {
 uint16_t result;
 ((uint8_t *)&result)[0] = f.read(); // LSB
 ((uint8_t *)&result)[1] = f.read(); // MSB
 return result;
}
uint32_t read32(File f) {
 uint32_t result;
 ((uint8_t *)&result)[0] = f.read(); // LSB
 ((uint8_t *)&result)[1] = f.read();
 ((uint8_t *)&result)[2] = f.read();
 ((uint8_t *)&result)[3] = f.read(); // MSB
 return result;
}
dlu
1,6612 gold badges15 silver badges29 bronze badges
asked Jan 13, 2016 at 14:35
4
  • 5
    Please edit your code down to the minimum required to demonstrate and debug the problem. Commented Jan 13, 2016 at 18:36
  • Do you want the buttons and processes to act independently from each other, or do you want some kind of blocking? Like "Press Coolant" runs does the coolant for 1000ms, but does or does not allow "Stop" to be pressed? do the buttons block each other? Commented Jan 13, 2016 at 21:34
  • For "Spindle Button" should what should happen if you hold your finger on it for 10 seconds? Start up spin, make the button be 'spinen.bmp' for one loop(), then display 'spinerr.bmp' for 1000ms, then display 'spinstp.bmp', shut off the spin, and repeat? I'd keep a state variable for the screen handling a touch so you don't cycle/race, and add it to the if(p...) condition. I think I'd move the button-de-animation out of the if(p....) condifional. Commented Jan 13, 2016 at 22:04
  • The whole idea is to give a visual representation of the device doing "something". My thinking was to time how long it takes the spindle to spin down and stop, use that time for the timer.. So an example would be, the spinde takes 5 seconds to slow down and stop, when user push the button, the command to stop the spindle is sent, the button is redrawn from green(on) to yellow(slowing) then to red(stopped) after 5secs. same for coolant, and homing. I'm trying to use something other than delay, as i need serial to update the X, Y and Z positions. and have access to stop in case of emergency Commented Jan 14, 2016 at 0:08

2 Answers 2

2

The usual technique at this scale of technology is to keep loop() pretty short, mostly making frequent calls to a timer function that keeps a list of things to be done (implemented with callback-functions) and the times at which to do them, and which calls those functions at the appropriate times. The upper level(s) of your sketch would schedule tasks by registering one or more callback-functions to be run at a given time or periodically.

When the timer function is called, if there is nothing to do, or when a callback-function returns, the timer function returns immediately to its caller (probably loop()). loop() can do other work, as long as it can still call the timer function as frequently as your task dispatching requires.

The SimpleTimer library at Arduino Playground is an implementation of such a timer-function.

answered Jan 13, 2016 at 19:51
0

Taking JRobert's advice, i checked out the simpletimer library.. the final solution was to set up three timers and change the way the buttons functioned.

as per documentation of the simpletimer library, added the following global:

#include <SimpleTimer.h>
SimpleTimer timer;
...

Then to setup, i added my three timers

timer.setInterval(7000, spindle); //times based on hardware reality
timer.setInterval(2000, coolant);
timer.setInterval(21500, homer);
...

Then started the timers in loop by adding:

timer.run();
...

Finally, i edited the buttons and added variables to denote state of button. i.e.

/*
0 == on, 
1 == transitional1 
2 == transitional2 
3 == stopped
*/
int homeb = 0;
int cool = 0;
int spin = 0;

Changed the touch screen code for each button to kick off the timers...

//Coolant Button
if (p.y > 110 && p.y < 170 && p.x > 250 && p.x < 300) { // if stop area is pressed
 if(cool == 3){
 bmpDraw("coolen.bmp", 15, 105);
 //PWM the spindle pin on
 cool = 0;
 } else {
 if(cool == 0){
 //PWM the spindle pin off
 cool = 1; //Start the timer sequence for stopping coolant
 }
 }
}

then i added a void for each timer, this is where i redraw the buttons

void coolant() {
 if (cool == 1){
 bmpDraw("coolerr.bmp", 15, 105);
 cool = 2;
 } else {
 if (cool = 2){
 bmpDraw("coolstp.bmp", 15, 105);
 cool = 3;
 }
 }
}

Hope this helps someone else in the future, as it was a real pickle for me to figure out at first.. Thanks again, and credit goes to JRobert, his advice pointed me directly to the solution.

answered Jan 14, 2016 at 2:13
1
  • Glad to have helped - and thanks for posting your implementation. Commented Jan 14, 2016 at 19:34

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.