Message Definition
Message Direction | Method Name | Parameters |
< from Arduino to PC > from PC to Arduino |
This will be the fuinction name called |
Parameters can be of a variety of types. If the type is not specified then it is of type "byte" range is 0 to 255 |
example : > |
neoPixelSetAnimation |
/deviceId/animation/red/green/blue/b16 speed |
Schema Datatypes
Schema | Arduino | Java | Range |
---|---|---|---|
byte | int | 1 byte - 0 to 255 | |
bool | bool | boolean | 1 byte - 0 to 1 |
b16 | int | int | 2 bytes -32,768 to 32,767 |
b32 | long | long | 4 bytes -2,147,483,648 to 2,147,483, 647 |
bu32 | unsigned long | long | 0 to 4,294,967,295 |
str | byte[ ] | String | variable length |
[ ] | byte[ ] | int [ ] | variable length |
Example 1. Arduino --to--> MrlComm
Given this Schema entry - > i2cWrite/deviceId/deviceAddress/[ ] data
This method is an i2cWrite function which writes to an I2C device on MrlComm. It requires a deviceId of the appropriate MrlComm device, and a device address on the I2C bus with the "data" to be written.
The following code is generated in the Msg.cpp class. A case where the message is identified will cause the Msg class to call the MrlComm's "expected" function signature with the correct parameters.
case I2C_WRITE: { // i2cWrite mrlComm->i2cWrite(ioCmd[1], /*deviceId*/ ioCmd[2], /*deviceAddress*/ ioCmd[3] /*dataSize*/, (byte*)(ioCmd+4) /*data*/); break; }
The header of Msg.h & MrlComm.h is also generated.
void i2cWrite(const byte& deviceId, const byte& deviceAddress, const byte& dataSize, const byte*data);
So a corresponding i2cWrite with an appropriate parameter signature "must" be implemented in MrlComm.cpp
Example 2.
In the other direction < publishSensorData/deviceId/[] data
the following code is generated.
void Msg::publishSensorData(const byte deviceId, const byte* data, const byte dataSize) { write(MAGIC_NUMBER); write(1 + 1 + (1 + dataSize)); // size write(PUBLISH_SENSOR_DATA); // msgType = 28 write(deviceId); write((byte*)data, dataSize); flush(); }
The generated code makes no intermediate objects, instead it maintains the Msg functions to appropriately serialize and de-serialize the messages
Here is example of some of the contents of the One File Which Rules Them All
< publishMRLCommError/str errorMsg
> getBoardInfo
< publishBoardInfo/version/boardType
> analogWrite/pin/value
> controllerAttach/serialPort
> customMsg/[] msg
> deviceAttach/deviceType/str name/[] config
> deviceDetach/deviceId
> digitalWrite/pin/value
> disableBoardStatus
> disablePin/pin
> disablePins
> enableBoardStatus
> enableHeartbeat/bool enabled
Benefits of code generation
- Less time to develop - we only write the interesting parts of functions - not the boring mundane and error prone serialization/deserialization code
- Less errors - parsers are always one of the most difficult to make and maintain correctly
- Less code - less code = less bugs !
- More structured - more structure means its easier to quickly understand
- Easier testing
This is the "exact" set of methods from PC->Arduino and all their method signatures which makes it easy to understand and test.
void getBoardInfo(); void controllerAttach(const byte& serialPort); void customMsg(const byte& msgSize, const byte*msg); void deviceAttach(const byte& deviceType, const byte& nameSize, const byte*name, const byte& configSize, const byte*config); void deviceDetach(const byte& deviceId); void disableBoardStatus(); void disablePin(const byte& pin); void disablePins(); void enableBoardStatus(); void enableHeartbeat(const bool& enabled); void enablePin(const byte& pin); void heartbeat(); void i2cRead(const byte& deviceId, const byte& deviceAddress, const byte& size); void i2cWrite(const byte& deviceId, const byte& deviceAddress, const byte& dataSize, const byte*data); void i2cWriteRead(const byte& deviceId, const byte& deviceAddress, const byte& readSize, const byte& writeValue); void neoPixelSetAnimation(const byte& size, const byte& animation, const byte& red, const byte& green, const byte& blue, const int& speed); void neoPixelWriteMatrix(const byte& deviceId, const byte& bufferSize, const byte*buffer); void sensorPollingStart(const byte& deviceId); void sensorPollingStop(const byte& deviceId); void servoDetach(const byte& deviceId); void servoSetMaxVelocity(const byte& deviceId, const int& maxVelocity); void servoSetVelocity(const byte& deviceId, const int& velocity); void servoSweepStart(const byte& deviceId, const byte& min, const byte& max, const byte& step); void servoSweepStop(const byte& deviceId); void servoWrite(const byte& deviceId, const byte& target); void servoWriteMicroseconds(const byte& deviceId, const int& ms); void setSerialRate(const long& rate); void softReset();
i2cReadWrite
HI GroG
i2cReadWrite is simply write a single byte first, then read.
Some i2c devices has memory. To read from memory, you first write one byte that is the pointer to the memory address where you want to start reading.
It's there to improve performance. But it's also possible to use i2cWrite to write one byte and then i2cRead to do the read. The result will be the same.
/Mats
Refactor mode now ... This
Refactor mode now ...
This crud is going away ...
Ultrasonic device
Hi GroG
Nice rework. Excited to see the result from the standardization and rework. Markus asked about how to use the ultrasonic sensor for distance measuring, and I don't think it was reworked this summer and I don't see the methods for it in the "exact" set of methods. Problably because all the code is an the MRLUltrasonic.h file and no MRLUltrasonic.cpp exists.
I guess it should be it's own device with it's own config and that it should use publishSensorData like all sensors ?
Just writing this so that it doesn't get "lost in translation".
/Mats
Yes Mats, you are correct. I
Yes Mats,
you are correct. I was surprised to see its non-implementation too.
It will need to be re-worked, but the new design I hope will make it trivial to make new (or re-work old) devices.
A SerialRelay device is also needed. I want to support Calamity's excellent feature, but its implementation needs to follow the rest of the design
Another thing not to get lost :)
So I've tested all "types"
So I've tested all "types" which was a pain .. but as far as I can tell there is no bugs doing rpc calls from
Java --to-> Arduino C++ or from
Arduino C++ --to--> Java
Here are the types supported and tested ..
With i2c writes & reads you are using Java byte[ ] which is signed (-128 to 127) .. Arduino's definition of byte == unsigned char (0 to 255)
I can convert your incoming bytes to unsigned equivalents.. which I 'think' would be the appropriate thing..
I'll check but I'm wondering if you do a conversion as well .. (the fewer the better in my opinion)
Hi Mats, I'm trying to get
Hi Mats,
I'm trying to get I2C to work in the new framework
This is curious
https://github.com/MyRobotLab/myrobotlab/blob/master/src/resource/Ardui…;
Its linking with a global ?
attachDevice()
It's the same construct as here:
https://github.com/MyRobotLab/myrobotlab/blob/master/src/resource/Ardui…
I think it can be removed since the allocation of the deviceId has moved from MRLComm to Java.