using an integer function return value to return a pointer - arduino

I am writing a serial command interpreter. The user will send a text string to the interpreter and it will do stuff and return an integer (either data or a code depending on what the user requested). But I want to expand the interpreter and allow the user to get an array of data or other structure in response to their query.
Can I use the integer return value to return a pointer to EEPROM (or global variable) address? And have the user follow the pointer to the memory location? Based on the query they sent, they would know if the return value is a pointer or data integer.
for example if I want to return
struct curve_t {
int type; // (2 bytes) calibration type indicator
int ref[2]; // (4 bytes) calibration reference point2
float param[11]; // (11*4 bytes) curve fitting parameters
} theCurve;
can I use a function like this?
int serialResponse(char * command) {
// interpret command here
return &theCurve;
}

Can you send a memory address through serial interface?
YES
Can your user access EEPROM through serial interface, using that address?
Not directly. Your MCU has to relay the data between your user and the EEPROM.

I wrote a small test program and confirmed that it is possible. I can pass the address from the function as an integer and then re-cast it in my calling function. It needs to address a global variable or at least on that is available in the calling function.
char res[10];
void loop {
b = function();
Serial.println((char *)b);
}
int function() {
return int(&res[0]);
}

I would not recommend casting a pointer into an integer because it won't work on computer architectures where an int has fewer bits than a pointer.
Lexical Parsers - like what you're writing - often arrange to return a token type, and place the token value in a union that the caller can access. The nice thing about structuring your code in that way is that it's extensible to whatever data types you want, and it will work no matter what C++ platform you're running on.
Here's an example of a token parser that can parse integers and your curve_t:
struct curve_t {
int type; // (2 bytes) calibration type indicator
int ref[2]; // (4 bytes) calibration reference point2
float param[11]; // (11*4 bytes) curve fitting parameters
};
union TokenValue {
int i; // type = TOKEN_TYPE_INT
struct curve_t *pCurve; // type = TOKEN_TYPE_P_CURVE
};
enum TokenType {
TOKEN_TYPE_UNKNOWN = 0,
TOKEN_TYPE_INT,
TOKEN_TYPE_P_CURVE
};
curve_t theCurve;
TokenValue tokenValue;
/*
* Parses the given command,
* setting the parsed value in tokenValue,
* returning the type of value (a TOKEN_TYPE_*).
*/
TokenType serialResponse(char * command) {
if (command[0] == 'a') { // TO DO: your code will test something else.
// We want to return an integer
tokenValue.i = 1234; // TO DO: in your code, instead set the integer value from command
return TOKEN_TYPE_INT;
}
if (command[0] == 'b') { // TO DO: your code will test something else.
// We want to return a pointer to theCurve.
// TO DO: Fill in the values of theCurve, for example theCurve.param[0]
tokenValue.pCurve = &theCurve;
return TOKEN_TYPE_P_CURVE;
}
// Else
return TOKEN_TYPE_UNKNOWN;
}
void setup() {
//TO DO: move this code to where it belongs in your Sketch
//TO DO: parse a command
char command[10] = "and so...";
// TO DO: read the command.
// Process the command
enum TokenType t;
t = serialResponse(command);
if (t == TOKEN_TYPE_INT) {
// The command result is an integer
int i = tokenValue.i;
// TO DO: process the integer.
} else if (t == TOKEN_TYPE_P_CURVE) {
// The command result is a curve
curve_t *pCurve = tokenValue.pCurve;
// TO DO: process the Curve.
} else {
// unrecognized command. TO DO: handle the error.
}
}
void loop() {
// put your main code here, to run repeatedly:
}
If you insist on using the cast of an int to a pointer (which I admit is a lot simpler), you could add a test for int size problems to your setup():
void setup() {
Serial.begin(9600);
if (sizeof(int) < sizeof(curve_t *)) {
Serial.println("cast won't work");
for (;;) {} // hang here forever.
}
}

Related

How to use Arduino's Serial.print with char* and const char* types

I require to build a simple Arduino function that returns either "char*" or "const char*" type and then I need to print that value.
However, I'm facing a problem: when I try to print the function's return value, actually nothing gets printed.
char *getID()
{
char ID[15]{"123456789ABCDE"};
// The actual value for ID is returned from another
// function as a String type, so, for simplicity's sake
// I'm just using a random string instead of posting here that function
String str{"EDCBA987654321"};
// Write the String returned value into the ID buffer
str.toCharArray(ID,str.length());
// The following piece of code actually prints the value: EDCBA987654321
//Serial.println("print ID from inside the function: ");
//Serial.println(ID);
return ID;
}
void setup() {
Serial.begin(9600);
while(!Serial);
}
void loop() {
/**
* Nothing gets printed when using the return value from the function
*/
Serial.println("print id as the value returned by the \"getID\" function:");
Serial.println(getID());
delay(2000);
}
This is the output on the serial monitor:
If I uncomment the lines inside the "getID" function, then the "ID" value gets printed:
I don't know what am I missing over here.
Thanks in advance and happy holidays.
There are two solutions for this, it all related to the fundamental understanding of string literal and array in C++, not specific to Arduino.
This will work:
char *getID()
{
char *ID{"123456789ABCDE"};
return ID;
}
In C++, a string literal has global scope, a pointer to a string literal which has global scope is of course point to the correct string literal in the memory. This is equivalent to directly using a global declared const char *ID{"123456789ABCDE"};.
or alternative this will also work:
char *getID()
{
static char ID[15]{"123456789ABCDE"};
return ID;
}
The problem with your original code is that ID[15] is an array which has local scope within the function, it is not a string literal, but merely an array of ID[15]{"1", "2", "3"... "E"};. Another problem is that you are returning a pointer to an array which immediately out of the scope when return. Therefore you need the modifier static to keep the array in memory even after returning from the function.

How do i store data from HTTPREAD into a variable?

I need a way to store HTTPREAD data into a variable because I will be comparing its value to another variable. Is there any way?
{
myGsm.print("AT+HTTPPARA=\"URL\",\"http://7ae0eae2.ngrok.io/get-ignition/ccb37bd2-a59e-4e56-a7e1-68fd0d7cf845"); // Send PARA command
myGsm.print("\"\r\n");
delay(1000);
printSerialData();
myGsm.println();
myGsm.println("AT+HTTPACTION=0");//submit the GET request
delay(8000);//the delay is important if the return datas are very large, the time required longer.
printSerialData();
myGsm.println("AT+HTTPREAD=0,17");// read the data from the website you access
delay(3000);
printSerialData();
delay(1000);
}
void printSerialData()
{
while(myGsm.available()!=0)
Serial.write(myGsm.read());
}
I am assuming that the Serial.write(myGsm.read()) is where you want to get the data from. In other words, you are receiving the data through the serial connection, and you want to parse the data returned from the AT+HTTPREAD command.
Since you did not provide any clue about what that command is returning in the serial, I gonna use as an example a different command that I know the output, the below one:
TX=> AT+CCLK?
RX=> AT+CCLK?\n\r
\t+CCLK: "2020/03/03, 22:00:14"\n\r
So, the string you are going to get from the above AT+CCLK? command is this (I am assigning to a char pointer for the sake of understanding):
char *answer = "AT+CCLK?\n\r\t+CCLK: "2020/03/03, 22:00:14"\n\r";
What you need is to parse the answer (the char *answer in this example) to get the "numbers" into variables.
How to do that?
You need to walk over that string, moving to specific places. For example, to be able to convert the 2020 into a variable, you need to be at position answer[19], and then you can use, let's say, the strtoul() to convert to an integer and store it into a variable.
uint32_t year = strtoul(&answer[19], NULL, 10);
Then, to get the month, you need to walk a bit more to reach the position at the month on the string:
uint32_t month = strtoul(&answer[24], NULL, 10);
And so on, but you are using magic numbers for that, in other words, the numbers 19, 24 are positions specific for this string.
Then, how to make this "walking" smarter?
You can use tokens in conjunction with the strstr() to go to the specific points you want in the string. In this case, we want to move the pointer to the first 2, so we can pass that pointer to the strtoul() to convert it into an integer.
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
int main() {
char *answer = "AT+CCLK?\n\r\t+CCLK: "2020/03/03, 22:00:14"\n\r";
char *token = "CCLK: \"";
char *ptr;
uint32_t year;
ptr = strstr(answer, token);
if (ptr == NULL) {
printf("Token not found\n");
return -1;
}
year = strtoul(++ptr, NULL, 10);
printf("Year = %d\n", year);
Then, to make this code into a function to be more generic, here it is:
bool parse_answer_to_uint32(char *buff, char *tokens[], uint32_t *val)
{
char *ptr;
int i;
if (val == NULL)
return false;
for (i = 0; buff != NULL && tokens[i] != NULL; i++) {
ptr = strstr(buff, tokens[i]);
if (ptr == NULL)
return false;
buff = (ptr + strlen(tokens[i]));
}
// Here, you reached the point you want, based on the tokens you seek
if (buff == NULL)
return false;
*val = strtoul(buff, NULL, 10);
}
So, you can be able to call this function like this:
char *tokens[] = { "CCLK: \"" };
uint32_t year;
if (parse_answer_to_uint32(myGsm.read().c_str(), tokens, &year) == false)
return -1;
printf("year is = %d\n", year);
The printf will print 2020 based on the example above.
This function is pretty flexible and generic enough. All you need is to pass different tokens to reach different points of the string and reach the value you want.
Take character buffer, Concat data comming from serial into this buffer, and process that buffer for comparison.

Understand how data is retrieved from function

I've started learning C for Arduino for about 2 weeks. I have the following code and I don't understand how data is retrieved from function ReadLine. Also I don't understand how variable BufferCount affects the program and why it is used. I do know that it holds the number of digits the year have but that's about all I know about this variable.
From what I've learned so far a function is composed of:
function type specifier
function name
function arguments.
What I see in this program makes me think that the function can also return values using the argument part. I always thought that a function can only return a value that is the same type (int, boolean ...) as the type specifier.
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.avaible() > 0) {
int bufferCount;
int year;
char myData[20];
bufferCount = ReadLine (myData);
year = atoi(myData); //convert string to int
Serial.print("Year: ");
Serial.print(year);
if (IsLeapYear(year)) {
Serial.print(" is ");
} else {
Serial.print(" is not ");
}
Serial.println("a leap year");
}
}
int IsLeapYear(int yr) {
if (yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) {
return 1; //it's a leap year
} else {
return 0;
}
}
int ReadLine (char str[]) {
char c;
int index = 0;
while (true) {
if (Serial.available() > 0) {
c = Serial.read();
if (c != '\n') {
str[index++] = c;
} else {
str[index] = '\0'; //null termination character
break;
}
}
}
return index;
}
The fundamental concept you are missing is pointers. In the case of a function like isLeapYear there, you'd be right about that parameter. It is just a copy of the data from whatever variable was passed in when the function gets called.
But with ReadLine things are different. ReadLine is getting a pointer to a char array. A pointer is a special kind of variable that holds the memory address of another variable. And it is true that in this case you are getting a local copy of the pointer, but it still points to the same location in memory. And during the function, data is copied not into the variable str, but to the memory location it points to. Since that is a memory location that belongs to a variable in the scope of the calling function, that actual variable's value will be changed. You've written over it in memory.

Arduino Dynamic Two-dimensional array

I'm working on an Arduino project where I need to build (and work with) a two-dimensional array at runtime. I've been poking around looking for a solution, but I've had no luck. I found an example of a dynamic one-dimentional array helper here: http://playground.arduino.cc/Code/DynamicArrayHelper, so i've been trying to adopt that code for my use. I created a library using the following code:
My Header file:
#ifndef Dynamic2DArray_h
#define Dynamic2DArray_h
#include "Arduino.h"
class Dynamic2DArray
{
public:
Dynamic2DArray( bool sorted );
//Add an integer pair to the array
bool add( int v1, int v2);
//Clear out (empty) the array
bool clear();
//Get the array item in the specified row, column
int getValue(int row, int col);
//Get the number of rows in the array
int length();
private:
int _rows;
void * _slots;
bool _sorted;
void _sort();
};
#endif
The library's code:
#include "Arduino.h"
#include "Dynamic2DArray.h"
#define ARRAY_COLUMNS 2
int _rows;
void * _slots;
bool _sorted;
Dynamic2DArray::Dynamic2DArray(bool sorted) {
//Set our local value indicating where we're supposed to
//sort or not
_sorted = sorted;
//Initialize the row count so it starts at zero
_rows = 0;
}
bool Dynamic2DArray::add( int v1, int v2) {
//Add the values to the array
//implementation adapted from http://playground.arduino.cc/Code/DynamicArrayHelper
//Allocate memory based on the size of the current array rows plus one (the new row)
int elementSize = sizeof(int) * ARRAY_COLUMNS;
//calculate how much memory the current array is using
int currentBufferSize = elementSize * _rows;
//calculate how much memory the new array will use
int newBufferSize = elementSize * (_rows + 1);
//allocate memory for the new array (which should be bigger than the old one)
void * newArray = malloc ( newBufferSize );
//Does newArray not point to something (a memory address)?
if (newArray == 0) {
//Then malloc failed, so return false
return false;
}
// copy the data from the old array, to the new array
for (int idx = 0; idx < currentBufferSize ; idx++)
{
((byte*)newArray)[idx] = ((byte *)_slots)[idx];
}
// free the original array
if (_slots != NULL)
{
free(_slots);
}
// clear the newly allocated memory space (the new row)
for (int idx = currentBufferSize; idx < newBufferSize; idx++)
{
((byte *)newArray)[idx] = 0;
}
// Store the number of rows the memory is allocated for
_rows = ++_rows;
// set the array to the newly created array
_slots = newArray;
//Free up the memory used by the new array
free(newArray);
//If the array's supposed to be sorted,
//then sort it
if (_sorted) {
_sort();
}
// success
return true;
};
int Dynamic2DArray::length() {
return _rows;
};
bool Dynamic2DArray::clear() {
//Free up the memory allocated to the _slots array
free(_slots);
//And zero out the row count
_rows = 0;
};
int Dynamic2DArray::getValue(int row, int col) {
//do we have a valid row/col?
if ((row < _rows) && (col < ARRAY_COLUMNS)) {
//Return the array value at that row/col
return _slots[row][col];
} else {
//No? Then there's nothing we can do here
return -1;
}
};
//Sorted probably doesn't matter, I can probably ignore this one
void _sort() {
}
The initial assignment of the _slots value is giving me problems, I don't know how to define it so this code builds. The _slots variable is supposed to point to the dynamic array, but I've got it wrong.
When I try to compile the code into my project's code, I get the following:
Arduino: 1.8.0 (Windows 10), Board: "Pro Trinket 3V/12MHz (USB)"
sketch\Dynamic2DArray.cpp: In member function 'int Dynamic2DArray::getValue(int, int)':
sketch\Dynamic2DArray.cpp:83:22: warning: pointer of type 'void *' used in arithmetic [-Wpointer-arith]
return _slots[row][col];
^
Dynamic2DArray.cpp:83: error: 'void*' is not a pointer-to-object type
Can someone please help me fix this code? I've posted the files to https://github.com/johnwargo/Arduino-Dynamic-2D-Array-Lib.
The code you took was for a 1D dynamic array; the modifications for a 2D array are too tricky. Give up these horrors.
I think there is no reason you use dynamic array. You can assume that size max is ROW_MAX * COL_MAX, so you can define a static array int array[ROW_MAX][COL_MAX].
on one hand if you defined a dynamic array, you could free space when you dont use it anymore and take advantage of it for other work. I dont know if this is your case.
on the other hand if you define a static array (on UNO), you have 32kB available on program space, instead of 2kB available on RAM.
Because of the difference 32kB / 2kB, there are very few chances you can get bigger array with dynamic allocation.

How can i specify an area of code to instrument it by pintool?

There are four levels of granularity in Pin: routine, instruction and image, trace.
Can i specify an limits/area to start and stop inserting instrumentation code.
may by like directive like ( # start instrumentation , # end instrumentation )
or some thing like that,
An example:
for( int i=0; i< x; i++)
{
#startInstrumentation
for( ....;.....;.....)
{
// some code
// function call, conditions , loops, ....
}
#endInstrumentation
}
Is there are any way to do this ?
You can use trace-based instrumentation to do what you want. At the beginning of each trace, check its start address and if it is not in range of interest, avoid adding analysis functions and return immediately from the routine.
It's possible that a trace will begin outside a region of interest but end inside it, or the other way around. If this can happen, you will need to perform more fine grained choice about what to instrument. I would check if this is a real concern before investing an effort.
If you're interested in instrumenting specific routines or images, consider using filter.cpp from InstLib in the kit. An example for use can be found in InstLibExamples.
Now, as for how to target these regions of interest, you have several options. If you have no control over the target binary, you can specify the region in a command line parameter, as a pair of offsets into the image of interest.
If you have control of the binary, you can insert two symbols, that specify the start and end of the rgion of interest, and then iterate over image symbols using the SYM interface.
My solution would be:
1) Insert Region Of Interest (ROI) begin and end functions in code
2) Set a flag after ROI begin is executed and unset it before ROI end is executed
3) Return immediately from instrumenting calls if the flat is unset
Here you have an example where I have modified the memory reference trace to trace only a ROI.
#include <stdio.h>
#include "pin.H"
#include <string>
const CHAR * ROI_BEGIN = "__parsec_roi_begin";
const CHAR * ROI_END = "__parsec_roi_end";
FILE * trace;
bool isROI = false;
// Print a memory read record
VOID RecordMemRead(VOID * ip, VOID * addr, CHAR * rtn)
{
// Return if not in ROI
if(!isROI)
{
return;
}
// Log memory access in CSV
fprintf(trace,"%p,R,%p,%s\n", ip, addr, rtn);
}
// Print a memory write record
VOID RecordMemWrite(VOID * ip, VOID * addr, CHAR * rtn)
{
// Return if not in ROI
if(!isROI)
{
return;
}
// Log memory access in CSV
fprintf(trace,"%p,W,%p,%s\n", ip, addr, rtn);
}
// Set ROI flag
VOID StartROI()
{
isROI = true;
}
// Set ROI flag
VOID StopROI()
{
isROI = false;
}
// Is called for every instruction and instruments reads and writes
VOID Instruction(INS ins, VOID *v)
{
// Instruments memory accesses using a predicated call, i.e.
// the instrumentation is called iff the instruction will actually be executed.
//
// On the IA-32 and Intel(R) 64 architectures conditional moves and REP
// prefixed instructions appear as predicated instructions in Pin.
UINT32 memOperands = INS_MemoryOperandCount(ins);
// Iterate over each memory operand of the instruction.
for (UINT32 memOp = 0; memOp < memOperands; memOp++)
{
// Get routine name if valid
const CHAR * name = "invalid";
if(RTN_Valid(INS_Rtn(ins)))
{
name = RTN_Name(INS_Rtn(ins)).c_str();
}
if (INS_MemoryOperandIsRead(ins, memOp))
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)RecordMemRead,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_ADDRINT, name,
IARG_END);
}
// Note that in some architectures a single memory operand can be
// both read and written (for instance incl (%eax) on IA-32)
// In that case we instrument it once for read and once for write.
if (INS_MemoryOperandIsWritten(ins, memOp))
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)RecordMemWrite,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_ADDRINT, name,
IARG_END);
}
}
}
// Pin calls this function every time a new rtn is executed
VOID Routine(RTN rtn, VOID *v)
{
// Get routine name
const CHAR * name = RTN_Name(rtn).c_str();
if(strcmp(name,ROI_BEGIN) == 0) {
// Start tracing after ROI begin exec
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)StartROI, IARG_END);
RTN_Close(rtn);
} else if (strcmp(name,ROI_END) == 0) {
// Stop tracing before ROI end exec
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)StopROI, IARG_END);
RTN_Close(rtn);
}
}
// Pin calls this function at the end
VOID Fini(INT32 code, VOID *v)
{
fclose(trace);
}
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage()
{
PIN_ERROR( "This Pintool prints a trace of memory addresses\n"
+ KNOB_BASE::StringKnobSummary() + "\n");
return -1;
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
int main(int argc, char *argv[])
{
// Initialize symbol table code, needed for rtn instrumentation
PIN_InitSymbols();
// Usage
if (PIN_Init(argc, argv)) return Usage();
// Open trace file and write header
trace = fopen("roitrace.csv", "w");
fprintf(trace,"pc,rw,addr,rtn\n");
// Add instrument functions
RTN_AddInstrumentFunction(Routine, 0);
INS_AddInstrumentFunction(Instruction, 0);
PIN_AddFiniFunction(Fini, 0);
// Never returns
PIN_StartProgram();
return 0;
}

Resources