GPIO control via GUI

Hi Everyone,

I got my first Core last week and was straight into playing with tinker and some of the online examples.
Now it’s time for my own project, what I’m trying achieve is on the face of it probably quite simple for most people here.
But i’m a noobie to the spark core and only just started learning C++

I want to have 8 x opto couplers controlled by the digital GPIO pins.
The hardware side is very simple and I’ve already tested a simplified circuit with a few optos by using the tinker app.

The functionality I need is :

1-The user sets a possible delay for each of the outputs in the range of 0 to 1000ms, this information is uploaded to the core.

2-The user then sends one GO! signal to the core over wifi from a mac/pc or smartphone and all outputs are fired with the previously uploaded delay settings if any.

I can just about figure out how to do the above as a “one shot deal” by using online build and flashing the core every time I want to change those delay times.
But I would like to build a simple GUI in which the delays could be set and have a GO button.
Could anyone point me to some examples of anything similar or any suggestions as to what the best method may be?

The “playback” needs to be reliable and very accurate, I have read various posts here saying that the Spark’s cloud and wlan functions can possibly cause timing issues with code that is running on the core. Is this the case and how would it be best avoided ?

Eventually I would like to use the 8 analog pins to control another 8 opto couplers as well, could these GPIO be run in a similar manner or at the same speed ?
I’ve read various things about the analog GPIOs, I understand that they are not truly analog outputs but use pwm, so in theory i could just write 0 or 255 to turn an opto connected to them off/on.
Is there anyway to simplify the way I would want to use these pins ?

I know I have a lot to learn so thanks in advance for any advice.

Cheers,
mala

definitely abstract the gui from the backend e.g. write the backend as a tcp server that responds to sending “pin1, HIGH” or something, then your GUI can be a webpage, android app or desktop client using something like Qt or GTK+

easy to test that way too - you can just use netcat to echo a string to a port e.g.

echo -n 1HIGH | nc -n -q1 192.168.0.2 23

so you can have your backend working and testable before you even have to worry about the gui.

Can’t you just use the Cloud instead of a TCP server? Haven’t got a clue what a TCP server might be, but can’t image this example to be too difficult to be handled by the cloud.
I was thinking you’d need a Spark.function with which you assign the delays, and another to act as the GO!
I’d start of simple. First try to get your Core code up and running, and then worry about the GUI. This topic is really useful if you don’t want to worry about the GUI side. It lists your functions and variables, and you can activate them by pressing a button. Really neat for development! Once you’ve got that working nicely, you could try working towards a GUI. JS should be good I guess. Take a look at some of the tutorials about sending values, they might prove useful.

Then again, I could be mistaken, but that’s how I’d try it.
Good luck, and don’t hesitate to ask if you need any further help.

1 Like

I’m with @Moors7 here, I would use a variable to hold the delays - encode them as a comma separated list, e.g.

`0,1000,200,250,0,800,500,1000’

You can then send this variable to the spark using the cloud api. This makes it easy to hook up the behaviour to a front end running the UI and a bit of javascript. On the spark, you’d split the received string at commas using strtok, and convert the individual values from strings to integers using atoi(), which you store in an array.

To start the display, again, using the cloud, I’d expose a playback function, which you can call using the cloud function api.. On the spark, this function could work like this:

void playback(String args) {    // the function called, with the list of delays 
   int delays[8];     // 8 delays, parsed from the string passed to the function 
   parseDelays(delayVariable, delays);   // split the string and store values in an array   

   uint32_t start = millis();                      // time corresponding to offset 0
   for (;;) {
      uint32_t now = millis()-start;            // find the current time after the start
      for (int i=0; i<8; i++) {
          digitalWrite(i, delay[i]<=now);     // if the delay is less or equal than now, turn the output on
      }

      if (now>=1000)    // are we done?
          break;
   }
}

void setup() {
   Spark.function("playback", playback);
}

This isn’t complete code, just a sketch to show you a simple way to tackle the problem. There are certainly plenty of ways to improve this code, but I wanted to keep it simple at the start!

Regarding timing, the total delay is a second, so your code wont be interrupted by the cloud once playback starts. The code above should run with accuracy down to the millisecond.

When extending to using the 8 analog pins, I believe you can use digitalWrite() on these also.

Hope this helps!

I don't suppose you have an example of this? I've been trying to figure this out since you've posted it, but haven't managed it so far. And I'm slowly going crazy over it.
I'd like to parse some RGB values, so set my LEDs, but can't get it to work. It'd be greatly appreciated if you could help out a bit :innocent:

Thanks everyone for the replies this has given me plenty to go and investigate.

Howdy @moors7,
Yes’s sure I’m glad to help out.

Let’s say you want to parse a list of numbers separated by commas:

const int value_count = 8; // the maximum number of values
int values[value_count];    // store the values here

char string[50];
strcpy(string, "123,456,789");  // the string to split
int idx = 0;
for (char* pt=strtok(string, ","); pt && idx<value_count; pt=strtok(NULL, ",")) {
   values[idx++] = atoi(pt);
}  

At the end of the code, the values array looks like this:

values[0] = 123;
values[1] = 456;
values[2] = 789;

And idx equals 3, so you know there are 3 values in the array, just in case the user passes in more or fewer values.

2 Likes

@mdma Thanks again for the help !

I am very slowly starting to understand some of the individual parts… I think.
Putting the whole puzzle together may take a while though, I wasn’t joking when I said I was noob :wink:

I think you want to flip your source and destination:

strcpy(string, "123,456,789");

Yes definitely! Thanks, fixed applied. I got a const cast error in the ide, but clicking on the error didn’t seem to take me to any line - I thought it was this, so swapped them (against my better judgement!), but of course the error was elsewhere, and flipping the args was the wrong choice! I knew it was wrong - it just looked weird!

1 Like

I’ve lost a lot of hair trying to figure this out !
But have managed to at least send values from terminal, get them split up into the array and I think check they are all there.
Now I just need to figure out how to get the delay values applied to the outputs.
Here’s my horribly messy code so far.

int ourDelays(String command);
char delayVariable[50];        // array to pass string command to
int playBack(String command);      
const int delay_count = 8;         //max number of delays
int delays[50];                   // array to store delays


void setup()
{
    Spark.function("setdelays" , ourDelays);
    Spark.variable("delays" , delayVariable , STRING);
    Spark.function("playback" , playBack);
}

void loop()
{
    //loopforever
}

//delay parsing
int ourDelays(String command)
{
    // find a way to get function string to delayVariable char array....
    //seems like there should be an easier way
    char charBuf[50];
    command.toCharArray(charBuf, 50);
    strcpy(delayVariable, charBuf);
    //something else here !?!?
    char string[50];
    strcpy(string , delayVariable);  // the string to split
    int idx = 0;
    for (char* pt=strtok(string, ","); pt && idx < delay_count; pt=strtok(NULL, ","))
     {
         delays[idx++] = atoi(pt);
     }
         if (idx == 8 )
         {
             return 2;
}
else return -2;
}

// playback control
int playBack(String command)
    {
    if(command == "go")
    {
    return 1;
    }
else return -1;
} 

Would be very grateful for any suggestions on making this more “efficient” / tidying this up.
Thanks again for help so far

edit… not sure why I can’t get this formatted nicely like you guys…what happened to the pretty colours ??

I have managed to tidy up my attempt at this section:

//delay parsing
int ourDelays(String command) 
{
    command.toCharArray(delayVariable,50);
    int idx = 0;
    for (char* pt=strtok(delayVariable, ","); pt && idx < delay_count; pt=strtok(NULL, ","))
     {
         delays[idx++] = atoi(pt);
     }
}

Although have now lost the ability to read back out the sent delay values using the spark.variable…

@mdma (or anyone else) could you explain this part

 uint32_t start = millis();                      // time corresponding to offset 0
   for (;;) {
      uint32_t now = millis()-start;            // find the current time after the start
      for (int i=0; i<8; i++) {
          digitalWrite(i, delay[i]<=now);     // if the delay is less or equal than now, turn the output on
      }

I can understand some of the logic of this and what should happen, but I can’t get it to work because I don’t understand what should be going in that first for loop, every attempt I’ve made so far ends up with either no outputs getting turned on or all outputs get turned on at the same time… i.e the delay is being ignored.
To my logic (probably wrong) the millis()start needs to be immediately after the playback command is determined to be true, then the uint32_t now = millis()-start needs to be within the second for loop with the digitalWrite…so it is getting continually updated ?
The other issue I have is that I want the outputs to be high just for 5-50ms then go low not to stay on, if I tag this on after the digitalWrite,i.e

delay(5);
digitalWrite(i, LOW);

then it ends up adding that delay time to the user set delay time as well…is there maybe some way to do this with a kind of macro or something ?
I’m so lost.

ah…come back to this two weeks later and realised that for loop is supposed to be empty…its an infinite loop?
I should probably learn some more C++ before posting again.

I now have a very simple version working, thanks everyone

That’s right: for (;;) can be read as “forever”

I’m now trying to figure out how to use your Flashee library to store the last delays sent by the user, so that if the spark is powered down it starts back up with the last settings.
So standby for a lot more questions! :wink:
first one … can I simply store the array of parsed delays ? rather than the sent string and have to parse it again ?

actually went for storing the sent string, then doing the parsing as part of playback function.
it might not be the best approach, but it’s working nicely and reliably.
So big thanks for Flashee :smile:

I still have to figure out a better way to turn the outputs off, at the moment they are all turned off at the end of the playback sequence.

for(int i=0;i<16;i++)
        {
        digitalWrite(outs[i], delays[i]<=now);
        digitalWrite(outs[i], LOW);
        }   

what I would prefer is that they are on for a set time only eg. 10ms… can anyone give me a hint ?

1 Like

So I just removed the ;

digitalWrite(outs[i], LOW);

and changed
delays[i]<=now to delays[i]==now

no idea how long the outs are on for now,but it can’t be that fast (or slow) as I can see a brief flash on the leds I have attached for testing :smiley:

I’ve been having quite a few problems with my core in the last two days for some strange reason that I don’t understand.

Code that has been working happily for a week, now does not work despite making no changes to it.
In particular a spark function I am using to write the delay settings to the core from terminal now just times out…
I have also tried the basic examples from the web IDE, blink led, netleds… which kind of worked but at one point trying to flash netleds to my core it became unresponsive part way through flashing (just stuck cycling magenta led) in the end I had to do a factory reset.
Everything seemed ok for a couple of hours but now the same problem is happening.
One thing I have noticed is that no matter what i’m flashing to the core the whole process is taking 5-8 times longer than it did before (in particular the magenta led blinking section)
Has there been a problem with the Build web IDE ? … I ask because at one point I had a error message in terminal saying it could not resolve " api.spark.io" FWIW my internet & WiFi connection (router) has been stable through out all this.

Here is the code that was happily working, it might not be the best or tidiest but do you think there’s something lurking in there that has caused damage to my core ?

// This #include statement was automatically added by the Spark IDE.
#include "flashee-eeprom/flashee-eeprom.h"
using namespace Flashee;

int outs[] = {A0, A1, A2, A3, A4, A5, A6, A7, D0, D1, D2, D3, D4, D5, D6, D7};
FlashDevice* flash1;
char delayVariable [85]; //array to store sent string of delays
char buf[85]; //flash read buffer
char *message = buf;// connect "report variable" to flash read buffer
const int delay_count =16; // max number of delays
int delays[delay_count] ; // store delays here

void setup()
{
    flash1 = Devices::createAddressErase();
    Spark.function("setdelays",saveDelays );
    Spark.function("playback", playBack);
    Spark.variable("report", message, STRING);
    for (int i=0;i<16;i++) {pinMode(outs[i],OUTPUT);}  // Outputs array setup
    flash1->read(buf,0,85);
}

int saveDelays(String command)
{
    command.toCharArray(delayVariable,85);
    delay(20);
    flash1->writeString(delayVariable, 0);
    return 1;
}

void loop()
{
    flash1->read(buf,0,85);
}

int playBack(String args)
{
    int idx = 0;  //parsing string to get delays
    for(char* pt=strtok(buf, ","); pt && idx < delay_count; pt=strtok(NULL, ","))
    {
        delays[idx++] = atoi(pt);
    }
 unsigned long startTime = millis(); // playback
    for(;;){
    unsigned long now = millis() - startTime;
    for(int i=0;i<16;i++)
        {
        digitalWrite(outs[i], delays[i]==now);
        //digitalWrite(outs[i], LOW);
        }
        if(now>=1000)  // max time *update*
    break;
   }
   return 2;
}

Thanks for any help!

How long is the overall transfer process taking (magenta flashing) ? 1 min 2min 5 min? what was it before? mine can take 4 to 5 minutes sometimes

It was taking no more than a minute before and is now sometimes taking up to 4 or 5 minutes