yun encode / decode json

Greetings!

The question is, what's the best way of handling json on an arduino yun?

I have a "project" with a web page, and 5 servos

I want to be able to set the value of the servo motors
And to send back the position of the servo motos for the page to display an "live" image of them.

I could make 5 requests to set/mx/value, but why make 5 requests (and 5 confirmations) when I can make one request.

So my question is, how can I handle json encode / decode with an Yun (on the linux side)

I want to be able to:

From the page, to send a json string and convert it in an array on arduino side
ex: { c : setPosition, m : { m1 : 343, m2 : 342 , m3 : 342 }}

And from the Arduino side, to convert an array (with the current position of the servo) to json string, and send it to the page

I can't help you with the JSON part of it, other than to say that it's best handled on the Linux side of things, and I'm sure someone versed in that aspect will be along to give you some hints.

Then there's the matter of sending/reading the servo positions. This will require some communications between the sketch and Linux sides of the Yun.

There are some pros and cons to the Bridge library, but I think this application is a reasonable use of the Bridge.put() and Bridge.get() mechanism. The Linux side reads and parses the JSON, extracts out the 5 servo positions, and "puts" those values to the shared Bridge storage, using a unique key for each motor ("m1", "m2", etc?)

The sketch is then very simple. In the loop() function, it steps through each servo: for each one, it calls Bridge.get() to read the servo position from the bridge, and write it to the servo output. That's all it needs to do.

The last known position of the servo is always accessible from the Bridge shared data store. The Linux side code can read that value and format up the data for the web page. One advantage of the Bridge is that it is easy to debug using a standard web browser:

  • http://arduino.local/data/put/m1/123 will store the value 123 under the key "m1"
  • http://arduino.local/data/get/m1 will return the value of the "m1" key
  • http://arduino.local/data/get will return the value of all keys stored in the Bridge

In Python, to use the bridge, you must have this at the beginning of your script:

sys.path.insert(0, '/usr/lib/python2.7/bridge/')
from bridgeclient import BridgeClient as bridgeclient
bc = bridgeclient()

After that, you can put a value to the shared store using:

     bc.put('m1', '123')

And you can read a value using:

value = bc.get('m1')

Note that the bridge only works with strings, you will need to do the conversions back and forth from string to integer.

Some skeleton code for the sketch: (lots of details left out)

void setup()
{
  Bridge.begin();
}

void loop()
{
  processServo("m1", Servo1);
  processServo("m2", Servo2);
  processServo("m3", Servo3);
  processServo("m4", Servo4);
  processServo("m5", Servo5);

  delay(50); // Give a little delay so the Bridge isn't swamped with communications.
  // Or do other processing at this time if you have more to do.
}

void processServo(string key, Servo servo)
{
  string value = Bridge.get(key); // Read the position
  int position = value.toInt();  // Convert to integer
  servo.write(position); // Output the servo position
}

The sketch doesn't write the current servo position back out, since the servo is open loop and we don't really know it's position. We just assume it's where we told it to go. If you do have an encoder or other feedback method, then you could read the value and put the value to another set of Bridge variables.

There are lots of optimizations that can be done. One is to keep track of the last known position of the servos in the sketch, and only write to the Servo output if the value has actually changed.

Another optimization is to have a single "new" key. When the Linux updates any of the motor positions, it sets the "new" key to "T". The sketch only reads the "new" key, and if it isn't "T" it does nothing else. If the "new" key is "T", it puts a value of "F" to the "new" key, and then processes the five servo positions. This method lightens the load on the bridge a bit, only polling one value most of the time rather than five.

There are quite still some details to work out, but that's the way I would approach it. Of course, there are lots of other ways. My thought is to do as much of the processing as possible in on the Linux side, and use the sketch to only do the actual hardware I/O operations. Some people don't like the Bridge, but this project is an ideal application for it: you need to store a limited set of data, and you are only interested int he current values. Why create a more complicated method when this one fits so well?

Thanks for your replay

But I think I should forget about json and keep it simple, with 'rest like' strings to pass data from / to the page.

I don't use urls to access the arduino, I use spacebrew to create a "connection" between the page and Arduino, and I can send / recive messages almost in real time.

To set all positions with one message I'll use

set/servos/p1/p2/p3/p4/p5

and just process the string to see what's there. if first parameter is set, and the second one is servos, the next 5 values are the servo positions.

To update the page with the "position" of the servo, it will send the same string, but with the "position" of the servo.

servoPosition/p1/p2/p3/p4/p5

On the page it's easy to explode the string by '/' and see what's there

It's not as elegant as json, but it does it's job.

chriissis:
To set all positions with one message I'll use

set/servos/p1/p2/p3/p4/p5

If you change the URL to be /arduino/servos/p1/p2/p3/p4/p5, then the URL will be sent directly to the sketch, and you can use a series of readStringUntil() calls, borrowing from the structure of the Bridge Example:

void loop() {
  // Get clients coming from server
  YunClient client = server.accept();

  // There is a new client?
  if (client) {
    // Process request
    process(client);

    // Close connection and free resources.
    client.stop();
  }

  delay(50); // Poll every 50ms
}


void process(YunClient client) {
  // read the command
  String command = client.readStringUntil('/');

  // is "servos" command?
  if (command == "digital") {
    servoCommand(client);
  }

  // Decode additional commands...
}


void servoCommand(YunClient client) {
   servo1.write(client.parseInt());
   servo2.write(client.parseInt());
   servo3.write(client.parseInt());
   servo4.write(client.parseInt());
   servo5.write(client.parseInt());

  // Send feedback to client
  client.println(F("Servos updated"));

  // Can print out individual servo positions here.
}

You will probably want to be a bit more sophisticated and look for error conditions, but that's the general idea.

This way, the RESTful API is handled entirely by the sketch, and no Linux programming is required. Normally, I like to try and do as much as possible on the Linux side, but your data format is so easy to parse.

To update the page with the "position" of the servo, it will send the same string, but with the "position" of the servo.

servoPosition/p1/p2/p3/p4/p5

I'm confused. Who or what is sending this?

Is the sketch sending this in response to the set servo position command? If so, you would just have to format and print this string at the end of servoCommand.

Or are you going to create another RESTful command that returns this string?

Or perhaps the Yun will receive this string, and parse it to see which data values you want and then build a string in that format? (that would be getting a bit complex.)

Just remember, the easy way to do a RESTful API is to have the token /arduino/ as the first part of the URL. Whatever follows on the URL gets passed to the sketch as a YunClient object. One gotcha is that it looks like any trailing slashes are stripped off, and CR/LF is always appended to the string. It shouldn't be a problem for the parseInt() calls, but might be a concern for other string parsing methods.

If you don't put /arduino/ (or /data/ or /mailbox/ as the first token, then it will be up to code on the Linux side to handle the URL. That may or may not be more complicated depending on your comfort level.

I use the SpacebrewYun library to comunicate to / from the webpage

There is a sb.monitor() in the loop to handle incomming messages.
And I use sb.send("Yun - Send", rawCommand + "/response/" + val) to send back strings to the page.

It's a lot faster than using Yun Client, and I use "REST Like" strings because it's easier to parse.
On arduino side there are "functions" to handle rest like urls, I just use that to parse the recived strings.

On the page side, it's even easier, just explode by "/" and I have an array with the elements.


I'll continue here, so I won't start a new thread.

I need to make some math for the servos (inverse kinematics for an robotic arm)
And it seems that the microprocessor can't really do that, and I must do it on the linux side.

What's the best way to get the sketch to run a "script" on linux side, with 3 parameters and to get the result back to the sketch?

It would be nice to use php for this, since I'm familiar with it.

chriissis:
What's the best way to get the sketch to run a "script" on linux side, with 3 parameters and to get the result back to the sketch?

"Best" is difficult to say, what is best for you depends on so many things. The easiest way is the Process class, which is designed for just that. You can pass your parameters on the command line, or by writing to the Process object once it's started, at which point the process will get it by reading STDIN. Anything the process writes to STDOUT can be read from the Process object by the sketch.

chriissis:
...
It would be nice to use php for this, since I'm familiar with it.

Here we go;-

Install php-cli:

opkg update
opkg install php5  php5-cli
opkg install php5-mod-sockets
opkg install php5-mod-json

Php code:

nano /mnt/sda1/bridge.php
chmod 755 /mnt/sda1/bridge.php
#!/usr/bin/php-cli
<?php
require ("/usr/lib/php/bridge/bridgeclient.class.php");
$firstValue = $argv[1];
$secondValue = $argv[2];
$thirdValue = $argv[3];
$client = new bridgeclient();
$inputfile="/mnt/sda1/input.csv";
$inputstr=file_get_contents($inputfile);
$inputstr="1,2,3";
$client->put("D12",$inputstr);
$outputstr=$firstValue.','.$secondValue.','.$thirdValue;
$outputfile="/mnt/sda1/output.csv";
file_put_contents($outputfile, $outputstr);
?>

ATmega32u4 code:

#include <Process.h>
void setup()
{
  Bridge.begin();
  while (!Serial);     // do nothing until the serial monitor is opened
  Serial.println("Start");
}
void loop()
{
  Process p;
  p.begin("/mnt/sda1/bridge.php");
  p.addParameter("4");
  p.addParameter("5");
  p.addParameter("6");
  p.run();
  char lbuffer[256];
  Bridge.get("D12", lbuffer, 256);
  //Serial.println(lbuffer);
  String str = String(lbuffer);
  int commaIndex = str.indexOf(',');
  int secondCommaIndex = str.indexOf(',', commaIndex + 1); //  Search for the next comma just after the first
  String firstValue = str.substring(0, commaIndex);
  String secondValue = str.substring(commaIndex + 1, secondCommaIndex);
  String thirdValue = str.substring(secondCommaIndex + 1); // To the end of the string
  Serial.println(firstValue);
  Serial.println(secondValue);
  Serial.println(thirdValue);
  delay(1000);
}

Thanks all for the answers guys!

This is what I ended with

The php file get the three parameters, and it echoes a string value1,value2,value3

(It seems to works, without the php bridge library)

void processIK(int x, int y, int u)
{
  Process p;
  String c = "";
  
  p.begin("/mnt/sd/arduino/www/processIK.php");
  p.addParameter("100");
  p.addParameter("150");
  p.addParameter("90");
  p.run();
  
  while(p.running()) {};
  
  while (p.available()>0) {
     c += p.readString();
  }
  
  int CommaIndex1 = c.indexOf(',');
  int CommaIndex2 = c.indexOf(',', CommaIndex1 + 1); //;
  
  String u1 = c.substring(0, CommaIndex1);
  String u2 = c.substring(CommaIndex1 + 1, CommaIndex2);
  String u3 = c.substring(CommaIndex2 + 1);
  
   Serial.println(u1);  
   Serial.println(u2);  
   Serial.println(u3);
}

chriissis:
...
This is what I ended with

The php file get the three parameters, and it echoes a string value1,value2,value3

(It seems to works, without the php bridge library)
...

Good method. as long as you don't need first pass value1,value2,value3 from AR9331 to ATmega32U4.