How to correctly use sscanf - arduino

I have to write an Arduino function to look up a number in the phone book. My code doesn't work because of the condition using sscanf. What am I doing wrong?
//read the phone book and put the good value in the string
void Read_ADMIN_PHONE_NUMBER(){
String ADMIN_PHONE_NUMBER;
delay(1000);
sendATcommand("AT+CPBS=\"SM\"", 500) ; //Select the SIM phonebook
sendATcommand("AT+CPBR=1,99", 100) ; // To read ALL phonebook +CPBR: 1,"690506990",129,"ANDROID"---- exemple de reponse copier du serial monitor
if (1 == sscanf(fonaInBuffer, "+CPBR: %*s", &ADMIN_PHONE_NUMBER)) {
Serial.println(F("*****"));
Serial.println(ADMIN_PHONE_NUMBER);
} else {
Serial.println(F("**bad***"));
}
delay(2000);
}
My problem come from the sscanf.I can't put fonaInBuffer in ADMIN_PHONE_NUMBER
void Read_ADMIN_PHONE_NUMBER(){
String ADMIN_PHONE_NUMBER;
delay(1000);
sendATcommand("AT+CPBS=\"SM\"", 500) ; //Select the SIM phonebook
sendATcommand("AT+CPBR=1,99", 100) ; // To read ALL phonebook like example: +CPBR:1,"690506990",129,"ANDROID"
if (1 == sscanf(fonaInBuffer, "+CPBR: %*s", &ADMIN_PHONE_NUMBER)) {
Serial.println(ADMIN_PHONE_NUMBER);
} else {
Serial.println(F("**bad***"));
}
delay(2000);

The * in %*s means 'do not assign', and such conversion specifications are not counted in the number of successful conversions. Therefore, the sscanf() call will return 0 always (unless the scanned string is empty; then it returns EOF) because there is no active conversion.
Remove the *, or replace it with a suitable number. If the ADMIN_PHONE_NUMBER is a char ADMIN_PHONE_NUMBER[123];, then the number you use should be (at most) 122: %122s — because sscanf() writes a null after the 122 characters if it reads 122 characters in a single 'word'. It is not clear what String ADMIN_PHONE_NUMBER; means — more precise advice cannot be given because of that.
Reducing your code to close to an MCVE (How to create a Minimal, Complete, and Verifiable Example?):
#include <stdio.h>
int main(void)
{
char buffer[] = "+CPBR: 1,\"690506990\",129,\"ANDROID\"";
char number[64];
int n = sscanf(buffer, "+CPBR: %63s", number);
printf("n = %d: number = [%s]\n", n, number);
n = sscanf(buffer, "+CPBR: %*d , \" %63[^\"]", number);
printf("n = %d: number = [%s]\n", n, number);
return 0;
}
Sample output:
n = 1: number = [1,"690506990",129,"ANDROID"]
n = 1: number = [690506990]
Take your pick as to which you want to use. I've been liberal with allowed white space in the sscanf conversion specifications. You can be less liberal if you prefer.

Related

Why do I need to send a message twice to trigger Indy's OnExecute event?

I am working on an application that works as a "man in the middle" to analyze a protocol (ISO 8583) sent over TCP/IP.
The main idea is to get the raw binary data and convert it to a string for parsing and decoding the protocol.
For this, I am using the TIdMappedPortTCP component.
I am testing with Hercules.
I am working with:
Windows 11 Home
Embarcadero® C++Builder 10.4 Version 27.0.40680.4203
Delphi and C++ Builder 10.4 Update 2
Indy 10.6.2.0
More context can be found in these questions:
Where can I find a fully working example of a TCP Client and Server for Indy in C++Builder?
Parsing bytes as BCD with Indy C++ Builder
The problem is that I have to send the message twice to trigger the OnExecute event. I think this might be length related but I haven't found the issue. Other than that the program does what is expected from it.
If I use this data in Hercules:
00 04 60 02
equivalent to:
"\x00\x04\x60\x02"
My program processes everything correctly:
Here is the code:
void __fastcall TForm1::MITMProxyExecute(TIdContext *AContext)
{
static int index;
TIdBytes ucBuffer;
UnicodeString usTemp1;
UnicodeString usTemp2;
int calculated_length;
// getting the length in Hexa
calculated_length = ReadMessageLength(AContext);
// reads data
AContext->Connection->IOHandler->ReadBytes(ucBuffer, calculated_length);
// displays string with calculated length and size of the data
usTemp2 = UnicodeString("calculated length = ");
usTemp2 += IntToStr(calculated_length);
usTemp2 += " ucBuffer.Length = ";
usTemp2 += IntToStr(ucBuffer.Length);
Display->Lines->Add(usTemp2);
// converts the binary data into a a Hex String for visualization
usTemp1 = BytesToHexString(ucBuffer);
// adds an index to distinguish from previous entries.
usTemp2 = IntToStr(index);
usTemp2 += UnicodeString(": ");
usTemp2 += usTemp1;
Display->Lines->Add(usTemp2);
index++;
}
Here is the code for the functions called there. By the way, is there a better way to convert the bytes to a hex string?
// Convert an array of bytes to a hexadecimal string
UnicodeString BytesToHexString(const TBytes& bytes)
{
// Create an empty UnicodeString to store the hexadecimal representation of the bytes
UnicodeString hexString;
// Iterate through each byte in the array
for (int i = 0; i < bytes.Length; i++)
{
// Convert the byte to a hexadecimal string and append it to the result string
hexString += IntToHex(bytes[i], 2);
}
// Return the hexadecimal string
return hexString;
}
// Read the first two bytes of an incoming message and interpret them as the length of the message
int ReadMessageLength(TIdContext *AContext)
{
int calculated_length;
// Use the 'ReadSmallInt' method to read the length of the message from the first two bytes
calculated_length = AContext->Connection->IOHandler->ReadSmallInt();
// converting from hex binary to hex string
UnicodeString bcdLength = UnicodeString().sprintf(L"%04x", calculated_length);
// converting from hex string to int
calculated_length = bcdLength.ToInt();
// decrease length
calculated_length -= 2;
return calculated_length;
}
UPDATE
I have created a class to update the TEditRich control. But the problem persist, I need to send the message twice to be processed and the application freezes when trying to close it. This is my class:
class TAddTextToDisplay : public TIdSync {
private:
UnicodeString textToAdd;
public:
__fastcall TAddTextToDisplay(UnicodeString str) {
// Store the input parameters in member variables.
textToAdd = str;
}
virtual void __fastcall DoSynchronize() {
if (textToAdd != NULL) {
// Use the input parameters here...
Form1->Display->Lines->Add(textToAdd);
}
}
void __fastcall setTextToAdd(UnicodeString str) {
textToAdd = str;
}
};
And this is how my new OnExecute event looks:
void __fastcall TForm1::MITMProxyExecute(TIdContext *AContext) {
static int index;
TIdBytes ucBuffer;
UnicodeString usTemp1;
UnicodeString usTemp2;
int calculated_length;
int bytes_remaining;
// getting the length in Hexa
calculated_length = ReadMessageLength(AContext);
if (!AContext->Connection->IOHandler->InputBufferIsEmpty()) {
// reads data
AContext->Connection->IOHandler->ReadBytes(ucBuffer, calculated_length);
// displays string with calculated length and size of the data
usTemp2 = UnicodeString("calculated length = ");
usTemp2 += IntToStr(calculated_length);
usTemp2 += " ucBuffer.Length = ";
usTemp2 += IntToStr(ucBuffer.Length);
TAddTextToDisplay *AddTextToDisplay = new TAddTextToDisplay(usTemp2);
AddTextToDisplay->Synchronize();
// converts the binary data into a a Hex String for visualization
usTemp1 = BytesToHexString(ucBuffer);
// adds an index to distinguish from previous entries.
usTemp2 = IntToStr(index);
usTemp2 += UnicodeString(": ");
usTemp2 += usTemp1;
AddTextToDisplay->setTextToAdd(usTemp2);
AddTextToDisplay->Synchronize();
delete AddTextToDisplay;
index++;
}
}
You really should not be reading from the IOHandler directly at all. You are getting your communication out of sync. TIdMappedPortTCP internally reads from the client before firing the OnExecute event, and reads from the target server before firing the OnOutboundData event. In both cases, the bytes received are made available in the TIdMappedPortContext::NetData property, which you are not processing at all.
You need to do all of your parsing using just the NetData only, iterating through its bytes looking for complete messages, and saving incomplete messages for future events to finish.
Try something more like this instead:
#include <IdGlobal.hpp>
#include <IdBuffer.hpp>
bool ReadMessageData(TIdBuffer *Buffer, int &Offset, TIdBytes &Data)
{
// has enough bytes?
if ((Offset + 2) > Buffer->Size)
return false;
// read the length of the message from the first two bytes
UInt16 binLength = Buffer->ExtractToUInt16(Offset);
// converting from hex binary to hex string
String bcdLength = String().sprintf(_D("%04hx"), binLength);
// converting from hex string to int
int calculated_length = bcdLength.ToInt() - 2;
// has enough bytes?
if ((Offset + 2 + calculated_length) > Buffer->Size)
return false;
// reads data
Data.Length = calculated_length;
Buffer->ExtractToBytes(Data, calculated_length, false, Offset + 2);
Offset += (2 + calculated_length);
return true;
}
void __fastcall TForm1::MITMProxyConnect(TIdContext *AContext)
{
AContext->Data = new TIdBuffer;
}
void __fastcall TForm1::MITMProxyDisconnect(TIdContext *AContext)
{
delete static_cast<TIdBuffer*>(AContext->Data);
AContext->Data = NULL;
}
void __fastcall TForm1::MITMProxyExecute(TIdContext *AContext)
{
static int index = 0;
TIdBuffer *Buffer = static_cast<TIdBuffer*>(AContext->Data);
Buffer->Write(static_cast<TIdMappedPortContext*>(AContext)->NetData);
Buffer->CompactHead();
TAddTextToDisplay *AddTextToDisplay = NULL;
TIdBytes ucBuffer;
int offset = 0;
while (ReadMessageData(Buffer, offset, ucBuffer))
{
String sTemp = String().sprintf(_D("%d: ucBuffer.Length = %d ucBuffer = %s"), index, ucBuffer.Length, ToHex(ucBuffer).c_str());
if (AddTextToDisplay)
AddTextToDisplay->setTextToAdd(sTemp);
else
AddTextToDisplay = new TAddTextToDisplay(sTemp);
AddTextToDisplay->Synchronize();
++index;
}
delete AddTextToDisplay;
if (offset > 0)
Buffer->Remove(offset);
}
Otherwise, if you want to do your own socket I/O, then you will have to use TIdTCPServer and TIdTCPClient directly instead of using TIdMappedPortTCP.

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.

C++ Vector understanding

I am currently using codewars.com to practice my coding skills. I finished one of the problems and wanted to check what other people's solutions were and I found one I couldn't understand. It's much better than my solution and I would like to understand it more. such what does "*std" do exactly. what is the +=i doing to the min_elements and what is happening to the min elements?
long queueTime(std::vector<int> customers,int n){
std::vector<long> queues(n, 0);
for (int i : customers)
*std::min_element(queues.begin(), queues.end()) += i;
return *std::max_element(queues.cbegin(), queues.cend());
}
This was my solution:
#include <iostream>
#include <vector>
#include <array>
using namespace std;
long queueTime(std::vector<int> customers,int n){
int i = 0; //start of Queue
int count = 0; //keeps track of how many items has been
processed
int biggest = 0; //Last/largest ending item size, add to count at end
int list [n]; //Declared number of registers by size n
for(int k = 0;k<n;k++) //sets each existing register to have 0
items
{
list[k] = 0;
}
//Start of processing customers, ends when last customer is at register.
for (auto i = customers.begin(); i!=customers.end();)
{
//checks if there are free registers.
for(int index = 0; index<n && i!=customers.end();index++)
{
if(list[index]==0)
{
list[index] = *i;
i++;
}
}
//Subtract 1 from every register
int temp=0;
for (int k =0;k<n;k++)
{
if(list[k]!= 0)
{
temp = list[k];
temp = temp-1;
list[k] = temp;
}
}
//increase count of items processed
count++;
}
//calculates the largest number of items a customer has amungst the last few
customers.
for(int j=0;j<n;j++)
{
if(list[j]>biggest)
{
biggest = list[j];
}
}
//end first part
cout<<"\nCount: "<<count<<" Biggest: "<<biggest<<endl;
cout<<"End Function:"
<<"\n************************\n***************************
*******************\n"<<endl;
//answer if number of items processed + last biggest number of items.
return count+biggest;
}
The code is mapping a set of integers to n buckets and minimizing the sum of the elements assigned to a given bucket.
For each customer int i , the smallest element of the queue is incremented by i. Then the largest resulting queue value is returned.
std::vector is a qualified name lookup of an identifier within the std namespace.
min_element returns an iterator. The dereference operator (*) produces an lvalue that is incremented by a compound assignment operator (+=).

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.

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