Receiving a string through UART in STM32F4 - microcontroller

I've written this code to receive a series of char variable through USART6 and have them stored in a string. But the problem is first received value is just a junk! Any help would be appreciated in advance.
while(1)
{
//memset(RxBuffer, 0, sizeof(RxBuffer));
i = 0;
requestRead(&dt, 1);
RxBuffer[i++] = dt;
while (i < 11)
{
requestRead(&dt, 1);
RxBuffer[i++] = dt;
HAL_Delay(5);
}
function prototype
static void requestRead(char *buffer, uint16_t length)
{
while (HAL_UART_Receive_IT(&huart6, buffer, length) != HAL_OK)
HAL_Delay(10);
}

First of all, the HAL_Delay seems to be redundant. Is there any particular reason for it?
The HAL_UART_Receive_IT function is used for non-blocking mode. What you have written seems to be more like blocking mode, which uses the HAL_UART_Receive function.
Also, I belive you need something like this:
Somewhere in the main:
// global variables
volatile uint8_t Rx_byte;
volatile uint8_t Rx_data[10];
volatile uint8_t Rx_indx = 0;
HAL_UART_Receive_IT(&huart1, &Rx_byte, 1);
And then the callback function:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == UART1) { // Current UART
Rx_data[Rx_indx++] = Rx_byte; // Add data to Rx_Buffer
}
HAL_UART_Receive_IT(&huart1, &Rx_byte, 1);
}
The idea is to receive always only one byte and save it into an array. Then somewhere check the number of received bytes or some pattern check, etc and then process the received frame.
On the other side, if the number of bytes is always same, you can change the "HAL_UART_Receive_IT" function and set the correct bytes count.

Related

AsyncTCP on ESP32 and Odd Heap/Socket Issues w/SOFTAP

I'm struggling with an issue where an ESP32 is running as a AP with AsyncTCP connecting multiple ESP32 clients. The AP receives some JSON data and replies with some JSON data. Without the handleData() function, the code runs 100% fine with no issues. Heap is static when no clients connect and issues only occur when clients start connecting.
Can anyone see anything with my code that could be causing heap corruption or other memory weirdness?
static void handleData(void* arg, AsyncClient* client, void *data, size_t len) {
int i = 0, j = 0;
char clientData[CLIENT_DATA_MAX];
char packetData[len];
char *packetBuf;
packetBuf = (char *)data;
clientData[0] = '\0';
for (i=0;i <= len;i++) {
packetData[j] = packetBuf[i]; //packetBuf[i];
if ((packetData[j] == '\n') || (i == len)) {
packetData[j] = '\0';
if ((j > 0) && (packetData[0] != '\n') && (packetData[0] != '\r')) {
// See sensorData() below...
parseData.function(packetData, clientData);
if (clientData != NULL) {
// TCP reply to client
if (client->space() > 32 && client->canSend()) {
client->write(clientData);
}
}
}
j = 0;
} else
j++;
}
}
void sensorData(void *data, void *retData) {
StaticJsonDocument<CLIENT_DATA_MAX> fields;
StaticJsonDocument<CLIENT_DATA_MAX> output;
char sensor[15] = "\0";
char MAC[18] = "\0";
char value[20] = "\0";
bool sendOK = false;
memcpy((char *)retData, "\0", 1);
DeserializationError error = deserializeJson(fields, (char *)data, CLIENT_DATA_MAX);
if (error) {
DEBUG_PRINTLN(F("deserializeJson() failed"));
return;
}
if (fields["type"])
strcpy(sensor, fields["type"]);
switch (sensor[0]) {
case 'C':
if (fields["value"])
strcpy(value, fields["value"]);
sendOK = true;
break;
case 'T': //DEBUG_PRINT(F("Temp "));
setExtTempSensor(fields["value"]);
sendOK = true;
break;
case 'N':
output["IT"] = intTempC; //Internal temp
output["B1"] = battLevels[0];
serializeJson(output, (char *)retData, CLIENT_DATA_MAX-1);
break;
}
if (sendOK) {
output["Resp"] = "Ok";
serializeJson(output, (char *)retData, CLIENT_DATA_MAX-1);
}
strcat((char *)retData, "\n");
}
static void handleNewClient(void* arg, AsyncClient* client) {
client->setRxTimeout(1000);
client->setAckTimeout(500);
client->onData(&handleData, NULL);
client->onError(&handleError, NULL);
client->onDisconnect(&handleDisconnect, NULL);
client->onTimeout(&handleTimeOut, NULL);
}
void startServer() {
server = new AsyncServer(WIFI_SERVER_PORT);
server->onClient(&handleNewClient, &server)
}
Using AsyncTCP on the ESP32 was having multiple issues. Heap issues, socket issues, assert issues, ACK timeouts, connection timeouts, etc. Swapping to AsyncUDP using the exact same code as shown above with romkey's changes, resolved all of my issues. (Just using romkey's fixes did not fix the errors I was having with AsyncTCP.) I don't believe the issue is with AsyncTCP but with ESP32 libraries.
Either you should declare packetData to be of length len + 1 or your for loop should iterate until i < len. Because the index starts at 0, packetData[len] is actually byte len + 1, so you'll overwrite something random when you store something in packetData[len] if the array is only len chars long.That something random may be the pointer stored in packetBuf, which could easily cause heap corruption.
You should always use strncpy() and never strcpy(). Likewise use strncat() rather than strcat(). Don't depend on having done the math correctly or on sizes not changing as your code evolves. strncpy() and strncat() will guard against overflows. You'll need to pass a length into sensorData() to do that, but sensorData() shouldn't be making assumptions about the available length of retData.
Your test
if (clientData != NULL) {
will never fail because clientData is the address of array and cannot change. I'm not sure what you're trying to test for here but this if will always succeed.
You can just write:
char sensor[15] = "";
you don't need to explicitly assign a string with a null byte in it.
And
memcpy((char *)retData, "\0", 1);
is equivalent to
((char *)retData)[0] = '\0';
What's the point of declaring retData to be void * in the arguments to sensorData()? Your code starts out with it being a char* before calling sensorData() and uses it as a char* inside sensorData(). void * is meant to be an escape hatch for passing around pointers without worrying about their type. You don't need that here and end up needing to extra casts back to char* because of it. Just declare the argument to be char* and don't worry about casting it again.
You didn't share the code that calls handleData() so there may well be issues outside of these functions.

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.

using an integer function return value to return a pointer

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.
}
}

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;
}

processing + bitWrite + arduino

I am working with an Arduino and Processing with the Arduino library.
I get the error "The function bitWrite(byte, int, int) does not exist.";
it seams that processing + Arduino bitWrite function are not working together.
its raised due to this line:
arduino.bitWrite(data,desiredPin,desiredState);
my goal in this project is modifying a music reactive sketch to work with shift registers.
Here is my full code:
Arduino_Shift_display
import ddf.minim.*;
import ddf.minim.analysis.*;
import processing.serial.*;
import cc.arduino.*;
int displayNum = 8;
Arduino arduino;
//Set these in the order of frequency - 0th pin is the lowest frequency,
//while the final pin is the highest frequency
int[] lastFired = new int[displayNum];
int datapin = 2;
int clockpin = 3;
int latchpin = 4;
int switchpin = 7;
byte data = 0;
//Change these to mess with the flashing rates
//Sensitivity is the shortest possible interval between beats
//minTimeOn is the minimum time an LED can be on
int sensitivity = 75;
int minTimeOn = 50;
String mode;
String source;
Minim minim;
AudioInput in;
AudioPlayer song;
BeatDetect beat;
//Used to stop flashing if the only signal on the line is random noise
boolean hasInput = false;
float tol = 0.005;
void setup(){
// shift register setup
arduino.pinMode(datapin, arduino.OUTPUT);
arduino.pinMode(clockpin, arduino.OUTPUT);
arduino.pinMode(latchpin, arduino.OUTPUT);
arduino.digitalWrite(switchpin, arduino.HIGH);
//Uncomment the mode/source pair for the desired input
//Shoutcast radio stream
//mode = "radio";
//source = "http://scfire-ntc-aa05.stream.aol.com:80/stream/1018";
//mode = "file";
//source = "/path/to/mp3";
mode = "mic";
source = "";
size(512, 200, P2D);
minim = new Minim(this);
arduino = new Arduino(this, Arduino.list()[1]);
minim = new Minim(this);
if (mode == "file" || mode == "radio"){
song = minim.loadFile(source, 2048);
song.play();
beat = new BeatDetect(song.bufferSize(), song.sampleRate());
beat.setSensitivity(sensitivity);
} else if (mode == "mic"){
in = minim.getLineIn(Minim.STEREO, 2048);
beat = new BeatDetect(in.bufferSize(), in.sampleRate());
beat.setSensitivity(sensitivity);
}
}
void shiftWrite(int desiredPin, int desiredState)
// This function lets you make the shift register outputs
// HIGH or LOW in exactly the same way that you use digitalWrite().
// Like digitalWrite(), this function takes two parameters:
// "desiredPin" is the shift register output pin
// you want to affect (0-7)
// "desiredState" is whether you want that output
// to be HIGH or LOW
// Inside the Arduino, numbers are stored as arrays of "bits",
// each of which is a single 1 or 0 value. Because a "byte" type
// is also eight bits, we'll use a byte (which we named "data"
// at the top of this sketch) to send data to the shift register.
// If a bit in the byte is "1", the output will be HIGH. If the bit
// is "0", the output will be LOW.
// To turn the individual bits in "data" on and off, we'll use
// a new Arduino commands called bitWrite(), which can make
// individual bits in a number 1 or 0.
{
// First we'll alter the global variable "data", changing the
// desired bit to 1 or 0:
arduino.bitWrite(data,desiredPin,desiredState);
// Now we'll actually send that data to the shift register.
// The shiftOut() function does all the hard work of
// manipulating the data and clock pins to move the data
// into the shift register:
arduino.shiftOut(datapin, clockpin, MSBFIRST, data);
// Once the data is in the shift register, we still need to
// make it appear at the outputs. We'll toggle the state of
// the latchPin, which will signal the shift register to "latch"
// the data to the outputs. (Latch activates on the high-to
// -low transition).
arduino.digitalWrite(latchpin, arduino.HIGH);
arduino.digitalWrite(latchpin, arduino.LOW);
}
void draw(){
if (mode == "file" || mode == "radio"){
beat.detect(song.mix);
drawWaveForm((AudioSource)song);
} else if (mode == "mic"){
beat.detect(in.mix);
drawWaveForm((AudioSource)in);
}
if (hasInput){ //hasInput is set within drawWaveForm
for (int i=0; i<displayNum-1; i++){
if ( beat.isRange( i+1, i+1, 1) ){
shiftWrite(i, 1);
lastFired[i] = millis();
} else {
if ((millis() - lastFired[i]) > minTimeOn){
shiftWrite(i, 0);
}
}
}
}
} //End draw method
//Display the input waveform
//This method sets 'hasInput' - if any sample in the signal has a value
//larger than 'tol,' there is a signal and the lights should flash.
//Otherwise, only noise is present and the lights should stay off.
void drawWaveForm(AudioSource src){
background(0);
stroke(255);
hasInput = false;
for(int i = 0; i < src.bufferSize() - 1; i++)
{
line(i, 50 + src.left.get(i)*50, i+1, 50 + src.left.get(i+1)*50);
line(i, 150 + src.right.get(i)*50, i+1, 150 + src.right.get(i+1)*50);
if (!hasInput && (abs(src.left.get(i)) > tol || abs(src.right.get(i)) > tol)){
hasInput = true;
}
}
}
void resetPins(){
for (int i=0; i<ledPins.length; i++){
arduino.digitalWrite(ledPins[i], Arduino.LOW);
}
}
void stop(){
resetPins();
if (mode == "mic"){
in.close();
}
minim.stop();
super.stop();
}
BeatListener
class BeatListener implements AudioListener
{
private BeatDetect beat;
private AudioPlayer source;
BeatListener(BeatDetect beat, AudioPlayer source)
{
this.source = source;
this.source.addListener(this);
this.beat = beat;
}
void samples(float[] samps)
{
beat.detect(source.mix);
}
void samples(float[] sampsL, float[] sampsR)
{
beat.detect(source.mix);
}
}
You can achieve the same thing using standard bitwise operators. To turn a bit on:
data |= 1 << bitNumber;
The right-hand side (1 << bitNumber) is a bit-shift operation to create a suitable bit-mask. It takes the single '1' bit and moves it left until it reaches the desired position. The bitwise-or assignment (|=) combines that new bit-mask with the existing bits in data. This turns the desired bit on, but leaves the rest untouched.
The code to turn a bit off is slightly different:
data &= ~(1 << bitNumber);
You can see the same bit-shift operation here. However, it's preceded by the unary negation operator (~). This swaps all the 1's for 0's, and all the 0's for 1's. The result is the exact opposite of the bit-mask we used before. You can't do a bitwise-or operation this time though, or else you'll turn all the other bits on. The bitwise-and assignment (&=) is used instead to combine this mask with the data variable. This ensures the desired bit is turned off, and the rest are untouched.
In your code, desiredPin is the equivalent of bitNumber.
A full explanation of how bitwise operations work can be quite lengthy. I'd recommend looking for a good tutorial online if you need more help with that.
There are also the bitSet and bitClear Arduino macros that make the code a little more readable than bit shifting and using AND and OR. The format is either bitSet(what_to_modify,bit_number) and bitClear(what_to_modify,bit_number). These translate into very efficient code and can be used to manipulate both, variables and hardware registers. So for example, if you wanted to turn on pin 13 on the Arduino UNO, you would first need to look up that Arduino pin 13 is actually pin 5 on PORTB of the Atmel atmega328 chip. So the command would be:
bitSet(PORTB,5);

Resources