I wrote code to work with the MPU9250 (gyroscope and accelerometer), but I can’t read the data from another AK8963 device. There is a magnetometer on it, I get static data. In i2cdetect, I only have 0x68, i.e. the MPU9250. AK8963 at address 0x0C, but it is not there. Should it even be visible in i2cdetect?
I read the manual and the map register, it was said there that you need to enable Fuse ROM, since it will not work without it. I wrote the necessary data to the register, but the data is constantly static. All examples on the Internet with SPI or Arduino. How do I initialize AK8963 and read data? What file descriptor should be opened? 0x68 or 0x0C?
#define MPU_ADDRESS 0x68 //MPU9250 adress
#define I2C_FILE "/dev/i2c-1" //Locale i2c file
#define MPU_POWER1 0x6b
#define MPU_POWER2 0x6c
int main(){
int16_t gx, gy, gz;
int16_t ax, ay, az;
int16_t mx, my, mz;
//MPU9250
fd = open(I2C_FILE, O_RDONLY | O_WRONLY);
ioctl(fd, I2C_SLAVE, MPU_ADDRESS);
//AK8963
fd_ak8963 = open(I2C_FILE, O_RDONLY | O_WRONLY);
ioctl(fd_ak8963, I2C_SLAVE, 0x0C);
HxMagnetometer *magn = new HxMagnetometer(fd);
while(1){
magn->magn_get_raw(&mx, &my, &mz);
printf("mx = %d my = %d mz = %d\n", mx, my, mz);
printf("%s\n", _buffer);
//printf("GYRO: x = %d y = %d z = %d\n", gyro->gyro_get_x_raw(), gyro->gyro_get_y_raw(), gyro->gyro_get_z_raw());
delay(100);
}
return 0;
}
Class MGT
#include "HxMagnetometer.hpp"
HxMagnetometer::HxMagnetometer(int32_t id){
device_id = id;
i2c_smbus_write_byte_data(device_id, AK8963_CNTL, 0x00);
std::cout << "Power down magnetometer\n";
i2c_smbus_write_byte_data(device_id, AK8963_CNTL, 0x0F);
std::cout << "Enter Fuse ROM access mode\n";
delay(10);
std::cout << "X: " << i2c_smbus_read_byte_data(device_id, AK8963_ASAX) << std::endl;
std::cout << "Y: " << i2c_smbus_read_byte_data(device_id, AK8963_ASAY) << std::endl;
std::cout << "Z: " << i2c_smbus_read_byte_data(device_id, AK8963_ASAZ) << std::endl;
i2c_smbus_write_byte_data(device_id, AK8963_CNTL, MFS_16BITS << 4 | 0x02);
std::cout << "Set magnetometer data resolution and sample ODR:" << std::endl;
i2c_smbus_write_byte_data(device_id, AK8963_CNTL, 0x00);
std::cout << "Power down magnetometer\n";
}
void HxMagnetometer::magn_get_raw(int16_t* _x_magn_raw, int16_t* _y_magn_raw, int16_t* _z_magn_raw){
i2c_smbus_write_byte_data(device_id, AK8963_CNTL, 0x0F);
if (i2c_smbus_read_byte_data(device_id, AK8963_ST1) & 0x01)
{
std::cout << "Read status: ok\n";
*_x_magn_raw = magn_get_x_raw();
//Get y raw: 0, 1, 0
*_y_magn_raw = magn_get_y_raw();
//Get x raw: 0, 0, 1
*_z_magn_raw = magn_get_z_raw();
}
i2c_smbus_write_byte_data(device_id, AK8963_CNTL, 0x00);
//Get x raw: 1, 0, 0
// i2c_smbus_read_byte_data(device_id, AK8963_ST2);
// i2c_smbus_write_byte_data(device_id, AK8963_CNTL1,AK8963_FUSE_ROM);
// i2c_smbus_write_byte_data(device_id, AK8963_CNTL1,AK8963_PWR_DOWN);
}
//Get x raw
int16_t HxMagnetometer::magn_get_x_raw(void){
return BITWISE_SHIFT(i2c_smbus_read_byte_data(device_id, 0x03), i2c_smbus_read_byte_data(device_id, 0x04));
}
//Get y raw
int16_t HxMagnetometer::magn_get_y_raw(void){
return BITWISE_SHIFT(i2c_smbus_read_byte_data(device_id, 0x05), i2c_smbus_read_byte_data(device_id, 0x06));
}
//Get z raw
int16_t HxMagnetometer::magn_get_z_raw(void){
return BITWISE_SHIFT(i2c_smbus_read_byte_data(device_id, 0x07), i2c_smbus_read_byte_data(device_id, 0x08));
}
Related
I am trying to send some data and act on the reply. I see (using wireshark) that data is sent and received by the system, but boost::asio doesn't trigger my callback. Does somebody has an idea what I am doing wrong?
#include <asio.hpp>
#include <bits/stdint-uintn.h>
#include <chrono>
#include <condition_variable>
#include <cstddef>
#include <iostream>
#include <memory>
#include <mutex>
#include <string>
#include <system_error>
#include <thread>
static const int polynomial = 0x1021; // represents x^16+x^12+x^5+1
uint16_t calc(uint8_t* bytes, std::size_t length)
{
uint16_t new_crc = 0x0000;
// bytes part
for (std::size_t j = 0; j < length; ++j)
{
for (int i = 0; i < 8; ++i)
{
bool bit = ((bytes[j] >> (7 - i) & 1) == 1);
bool c15 = ((new_crc >> 15 & 1) == 1);
new_crc <<= 1;
// If coefficient of bit and remainder polynomial = 1 xor crc with polynomial
if (c15 ^ bit) new_crc ^= polynomial;
}
}
return new_crc;
}
int main(int argc, const char* argv[])
{
asio::io_service main_io_service;
std::string ip = "192.168.100.155";
int portP = 4001, portS = 4002;
auto sPrimary = std::shared_ptr<asio::ip::tcp::socket>(new asio::ip::tcp::socket(main_io_service));
auto sSecondary = std::shared_ptr<asio::ip::tcp::socket>(new asio::ip::tcp::socket(main_io_service));
auto epPrimary = asio::ip::tcp::endpoint(asio::ip::address::from_string(ip), portP);
auto epSecondary = asio::ip::tcp::endpoint(asio::ip::address::from_string(ip), portS);
std::error_code ec;
sPrimary->connect(epPrimary, ec);
if (ec || !sPrimary->is_open())
{
std::cerr << "primary failed to connect" << std::endl;
}
ec.clear();
sSecondary->connect(epSecondary, ec);
if (ec || !sSecondary->is_open())
{
std::cerr << "secondary failed to connect" << std::endl;
}
std::mutex mutex;
std::unique_lock<std::mutex> lock(mutex);
std::condition_variable cv;
const std::size_t msgSize = 9;
uint8_t msg[msgSize];
int i = 0;
msg[i++] = 0x02;
msg[i++] = 0xFF;
msg[i++] = 0x00;
msg[i++] = 0x00;
msg[i++] = 0x00;
msg[i++] = 0x00;
uint16_t crc = calc(msg, i);
msg[i++] = (uint8_t) (crc & 0xFF);
msg[i++] = (uint8_t) (crc >> 8);
msg[i++] = 0x03;
const std::size_t buffSize = 1024;
uint8_t buff[buffSize];
std::size_t bytesRead = 0;
asio::async_write((*sPrimary.get()), asio::buffer(msg, msgSize), [&sPrimary, &cv, &buff, &buffSize, &bytesRead](const std::error_code &ec, std::size_t bytesWritten)
{
asio::async_read((*sPrimary.get()), asio::buffer(buff, buffSize), [&cv, &bytesRead](const std::error_code &ec, std::size_t currentBytesRead)
{
bytesRead += currentBytesRead;
cv.notify_one();
});
});
main_io_service.run();
cv.wait(lock);
for (std::size_t i = 0; i < bytesRead; ++i)
std::cout << std::hex << buff[i];
main_io_service.stop();
return 0;
}
Just added the whole test code that will compile. Although you need a device that answers. This code talks to a serial server that has a piece of hardware that replies on the sent packet.
Thanks!
The problem that #rafix07 calls out is your problem.
Even if you "fake it out" by running io_service::run() on another thread, you technically still have a time window for the same race condition.
In general, locking synchronization primitives do not mix with task-based parallelism. In this case it would very much appear you just want to
post another task to the service when the read completes
expire a timer that you can respond to
In the very simple case of your code, you could even use other, simpler, options:
exploit the fact that run() blocks until all handlers completed. That is to say, you can take the sheer fact that run() returned as indication that the read completed:
don't use asynchronous handlers, since it doesn't serve a purpose (this could be down to oversimplified example code here)
4. use synchronous IO
This is by far the simplest. Many other simplifications made throughout the program
Live On Coliru
#include <cstdint>
#include <iostream>
#include <string>
#ifndef NOBOOST
#include <boost/asio.hpp>
namespace asio = boost::asio;
using boost::system::error_code;
#else
#include <asio.hpp>
namespace asio = boost::asio;
using std::error_code;
#endif
static const int polynomial = 0x1021; // represents x^16+x^12+x^5+1
uint16_t calc_crc(uint8_t* bytes, std::size_t length) {
uint16_t new_crc = 0x0000;
// bytes part
for (std::size_t j = 0; j < length; ++j) {
for (int i = 0; i < 8; ++i) {
bool bit = ((bytes[j] >> (7 - i) & 1) == 1);
bool c15 = ((new_crc >> 15 & 1) == 1);
new_crc <<= 1;
// If coefficient of bit and remainder polynomial = 1 xor crc with polynomial
if (c15 ^ bit)
new_crc ^= polynomial;
}
}
return new_crc;
}
int main() {
static const std::string ip = "127.0.0.1";
static const unsigned short portP = 4001, portS = 4002;
using asio::ip::address;
asio::io_service io;
asio::ip::tcp::socket sPrimary(io), sSecondary(io);
sPrimary.connect({ address::from_string(ip), portP });
sSecondary.connect({ address::from_string(ip), portS });
uint8_t msg[] {
0x02, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, //crc
0x03
};
{ // set crc
uint16_t const crc = calc_crc(msg, sizeof(msg)-3);
msg[sizeof(msg)-3] = (uint8_t)(crc & 0xFF);
msg[sizeof(msg)-2] = (uint8_t)(crc >> 8);
}
std::string buff;
auto bytesWritten = asio::write(sPrimary, asio::buffer(msg));
std::cout << bytesWritten << " sent" << std::endl;
auto bytesRead = asio::read(sPrimary, asio::dynamic_buffer(buff), asio::transfer_exactly(32));
std::cout << bytesRead << " received" << std::endl;
for (uint8_t ch : buff)
std::cout << std::hex << static_cast<int>(ch);
std::cout << std::endl;
}
Prints
9 sent
32 received
23696e636c756465203c63737464696e743ea23696e636c756465203c696f73
And indeed, that's the hex encoding of the first 32 bytes of main.cpp
3. use implicit completion
Trust that the handlers ran if run() returns (error handling would be required). The code is essentially the same but with more elaborate concerns around lambda captures and object lifetimes.
Note: all other simplifications still apply
Live On Coliru
asio::async_write(sPrimary, asio::buffer(msg), [&sPrimary, &buff](error_code ec, size_t bytesWritten) {
std::cout << "async_write: " << ec.message() << ", " << bytesWritten << " sent" << std::endl;
asio::async_read(sPrimary, asio::dynamic_buffer(buff), asio::transfer_exactly(32), [](error_code ec, size_t bytesRead) {
std::cout << "async_read: " << ec.message() << ", " << bytesRead << " received" << std::endl;
});
});
io.run();
for (uint8_t ch : buff)
std::cout << std::hex << static_cast<int>(ch);
std::cout << std::endl;
Prints:
async_write: Success, 9 sent
async_read: Success, 32 received
23696e636c756465203c63737464696e743ea23696e636c756465203c696f73
2. use a timer signal
This most closely "resembles" the CV approach you had, by using a timer object to represent the condition.
notably, this does error handling better than the above "3." code
note also, it guarantees to call the completion handler of signal_complete (unless the program terminates prematurely)
as such, the information is in the expiry() of the timer, not in the error code (the time will always appear canceled)
Live On Coliru
std::string buff;
asio::high_resolution_timer signal_complete(io, std::chrono::high_resolution_clock::time_point::max());
signal_complete.async_wait([&signal_complete, &buff](error_code ec) {
std::cout << "signal_complete: " << ec.message() << std::endl;
if (signal_complete.expiry() < std::chrono::high_resolution_clock::now()) {
for (uint8_t ch : buff)
std::cout << std::hex << static_cast<int>(ch);
std::cout << std::endl;
}
});
asio::async_write(sPrimary, asio::buffer(msg), [&sPrimary, &buff, &signal_complete](error_code ec, size_t bytesWritten) {
std::cout << "async_write: " << ec.message() << ", " << bytesWritten << " sent" << std::endl;
asio::async_read(sPrimary, asio::dynamic_buffer(buff), asio::transfer_exactly(32), [&signal_complete](error_code ec, size_t bytesRead) {
std::cout << "async_read: " << ec.message() << ", " << bytesRead << " received" << std::endl;
if (!ec) {
signal_complete.expires_at(std::chrono::high_resolution_clock::time_point::min());
} else {
signal_complete.cancel();
}
});
});
io.run();
Prints:
async_write: Success, 9 sent
async_read: Success, 32 received
signal_complete: Operation canceled
23696e636c756465203c63737464696e743ea23696e636c756465203c696f73
1. Post another task when read completes
This is the most natural fit to most async IO scenarios, because it puts all tasks in the same queue.
The only part that is further complicated is getting the life-times of (shared) objects right.
Live On Coliru
std::string buff;
asio::async_write(sPrimary, asio::buffer(msg), [&io, &sPrimary, &buff](error_code ec, size_t bytesWritten) {
std::cout << "async_write: " << ec.message() << ", " << bytesWritten << " sent" << std::endl;
asio::async_read(sPrimary, asio::dynamic_buffer(buff), asio::transfer_exactly(32), [&io, &buff](error_code ec, size_t bytesRead) {
std::cout << "async_read: " << ec.message() << ", " << bytesRead << " received" << std::endl;
if (!ec) {
post(io, [&buff] {
for (uint8_t ch : buff)
std::cout << std::hex << static_cast<int>(ch);
std::cout << std::endl;
});
}
});
});
io.run();
Printing, again:
async_write: Success, 9 sent
async_read: Success, 32 received
23696e636c756465203c63737464696e743ea23696e636c756465203c696f73
Recently, i've been assigned a client/server project, which is basically a chat room, where files can be sent and recieved, and we can use webcams.
I'm currently working on the file transfer part, and after looking at some online tutorials, i've noticed most of them use offsets to write into their buffers, then they write the whole buffer into their new file.
To replicate that kind of code, i've set up 2 buffers, one on the client side, the other on the server side. On the server side, i read 8192 bytes from my file, into the buffer, then i send it into the client side, which recieves it, and adds it to my buffer. Problem is, after the second file transfer, every single transfer it does, it's a SOCKET_ERROR, which probably means something's not quite right.
server:
std::ifstream readFile;
readFile.open(FileName, std::ios::binary | std::ios::ate);
if (!readFile)
{
std::cout << "unable to open file" << std::endl;
}
int FileSize = readFile.tellg();
readFile.seekg(0);
int remainingBytes = 0;
uint32_t FileSize32t = (uint32_t)FileSize;
FileSize32t = htonl(FileSize32t);
send(connections[ID], (char*)&FileSize32t, sizeof(uint32_t), 0);
int sent_bytes = 0;
int offset = 0;
char data[8192];
remainingBytes = FileSize;
int i = 0;
while (i<6)
{
readFile.read(data, 8192);
if (remainingBytes < 8192)
{
sent_bytes = send(connections[ID], data+offset, remainingBytes, 0);
remainingBytes -= sent_bytes;
offset += sent_bytes;
}
else
{
sent_bytes = send(connections[ID], data+offset, 8192, 0);
if (sent_bytes == SOCKET_ERROR)
{
std::cout << "erro" << std::endl;
}
remainingBytes -= sent_bytes;
offset += sent_bytes;
std::cout <<"offset: "<< offset << std::endl;
std::cout << "Sent bytes: " << sent_bytes << std::endl;
std::cout << "remaining bytes: " << remainingBytes << std::endl;
}
i++;
}
Client:
char data[8192];
std::ofstream writeFile;
writeFile.open("Putin.jpg", std::ios::binary);
int bytesReceieved = 0;
int totalBytesReceieved = 0;
int i = 0;
while (i<6)
{
if (recvFileSize - totalBytesReceieved < 8192)
{
bytesReceieved = recv(connection, data+totalBytesReceieved, recvFileSize - totalBytesReceieved, 0);
totalBytesReceieved += bytesReceieved;
}
else
{
bytesReceieved = recv(connection, data + totalBytesReceieved, 8192, 0);
totalBytesReceieved += bytesReceieved;
std::cout << totalBytesReceieved << std::endl;
}
i++;
}
writeFile.write(data, totalBytesReceieved);
std::cout << "transferência terminada, bytes recebidos: " << totalBytesReceieved << std::endl;
writeFile.close();
Do note that this is just a test program, and it's preety much one of my first interactions with C++. I've been told this probably isn't the best way to start off with C++, but i need this assignment done until the 15th of september, so i need to finish it regardless. If you find any errors or problems besides my original issue do feel free to point them out and if you can, explain me why it's wrong.
Thank you very much for your help.
I searched a lot and tried many different ways, but I cannot send data to gtkterm via virtual serial bridge (for testing!).
My idea is to communicate with an Atmega uC later on, but first I wanted to test the serial communication by setting up a virtual serial bridge with the help of soccat and controlling the output serial port with gtkterm. The problem is that I'm just receiving useless things in gtkterm... (see screenshots)
soccat command:
socat -d -d PTY: PTY:
The soccat virtual serial port bridge seems to be ok, because I can send data from one serial terminal to another...
gtkterm port preferences:
Port: /dev/pts/6
Baudrate: 9600
Parity: none
Bits: 8
Stopbits: 1
Flow control: none
My little GUI compiles and runs fine, with the input path "/dev/pts/6" and the input baudrate 9600. The program seems to run fine, but in gtkterm are just question marks and quadangles with symbols in every corner coming up. Let's say its not interpretable and independent by the signs as input, BUT the lengths of the output in gtkterm changes by the length of the input (the amount of signs I type in).
Finally here's my code:
main.cpp:
#include <iostream>
#include "serial/serial_communication.cpp"
std::string inputStringUser = "";
int inputIntUser = 0;
std::string pathSerial = "/dev/...";
int baudrate = 19200;
int main()
{
std::cout << "main() [communication_serial_uC] started..." << std::endl;
//GETTING STARTED
std::cout << "Use default port? " << pathSerial << " (Yes = y/ change port = insert the new path" << std::endl;
std::cin >> inputStringUser;
if(inputStringUser != "y" && inputStringUser != "Y")
pathSerial = inputStringUser;
std::cout << "Serial Port is set to: " + pathSerial << std::endl;
std::cout << "Use default baudrate? " << baudrate << "Yes = 0/ change baudrate = insert new baudrate" << std::endl;
std::cin >> inputIntUser;
if(inputIntUser > 0)
baudrate = inputIntUser;
std::cout << "Baudrate is set to: " << baudrate << std::endl;
Serial_communication myPort(pathSerial, baudrate);
//OPEN/ CONFIGURATE PORT
if(myPort.openPort(pathSerial, baudrate) < 0)
{
std::cout << "Error: opening" << std::endl;
return -1;
}
//WRITE PORT
std::cout << "Insert your 'message': (exit = 'exit')" << std::endl;
std::cin >> inputStringUser;
while(inputStringUser != "exit")
{
if(myPort.sendPort(inputStringUser) < 0)
{
std::cout << "Error: sending" << std::endl;
return -1;
}
std::cout << "Insert your 'message': (exit = 'exit')" << std::endl;
std::cin >> inputStringUser;
}
//CLOSE PORT
if(myPort.closePort() < 0)
{
std::cout << "Error: closing" << std::endl;
return -1;
}
std::cout << "main() [communication_serial_uC] beendet..." << std::endl;
return 0;
}
serial/serial_communication.hpp:
#include <iostream>
#include <string>
#include <cstring>
#include <fcntl.h>
#include <termios.h>
class Serial_communication{
public:
Serial_communication(std::string paramPathSerial, int paramBaudrate);
~Serial_communication();
int openPort(std::string pathSerial, int baudrate);
int sendPort(std::string testString);
int closePort();
private:
std::string pathSerial;
int baudrate;
//filedescriptors
int fd;
};
serial/serial_communcation.cpp
#include <iostream>
#include "serial_communication.h"
Serial_communication::Serial_communication(std::string paramPathSerial, int paramBaudrate)
{
fd = 0;
pathSerial = paramPathSerial;
baudrate = paramBaudrate;
}
Serial_communication::~Serial_communication()
{
}
int Serial_communication::openPort(std::string pathSerial, int baudrate)
{
std::cout << "openPort() [serial_communication] started with the following paramters... pathSerial = " << pathSerial << ", baudrate = " << baudrate << std::endl;
//OPENING PORT
//open serial port
fd = open(pathSerial.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if(fd < 0)
{
std::cout << "Error [serial_communcation]: opening Port: " << pathSerial << std::endl;
return -1;
}
//struct termios
struct termios serial, serial_old;
//get parameters associated with the terminal
if(tcgetattr(fd, &serial) < 0)
{
std::cout << "Error [serial_communication]: getting configuration" << std::endl;
return -1;
}
//safe old parameters
serial_old = serial;
std::cout << "[serial_communication]: Port opened" << std::endl;
//SERIAL CONFIGURATION
/* Set Baud Rate */
cfsetospeed (&serial, (speed_t)baudrate);
cfsetispeed (&serial, (speed_t)baudrate);
// Setting other Port Stuff
serial.c_cflag &= ~PARENB; // Make 8n1
serial.c_cflag &= ~CSTOPB;
serial.c_cflag &= ~CSIZE;
serial.c_cflag |= CS8;
serial.c_cflag &= ~CRTSCTS; // no flow control
serial.c_cc[VMIN] = 1; // read doesn't block
serial.c_cc[VTIME] = 5; // 0.5 seconds read timeout
serial.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
/* Make raw */
cfmakeraw(&serial);
/* Flush Port, then applies attributes */
tcflush( fd, TCIFLUSH );
//set attributes to port
if(tcsetattr(fd, TCSANOW, &serial) < 0)
{
std::cout << "Error [serial_communication]: set attributes" << std::endl;
return -1;
}
//CONFIGURATION FINISHED
std::cout << "openPort() [serial_communication] finished..." << std::endl;
return 1;
}
int Serial_communication::sendPort(std::string textString)
{
std::cout << "write() [Serial_communication] started with the following parameter... textString = " << textString << std::endl;
//attempt to send
if(write(fd, &textString, std::strlen(textString.c_str())) < 0)
{
std::cout << "Error [serial_communcation]: write";
return -1;
}
//SENDING FINISHED
std::cout << "write() [serial_communcation] finished..." << std::endl;
return 1;
}
int Serial_communication::closePort()
{
close(fd);
return 1;
}
So... thats all I got. I tried my best and joined the information from many websites and tried a lots of example codes. My problem is that I dont even know where to search for, so I'm appreciate for any clue...
If there are any questions or information missing, pls let me know it!
Thanks in advance
Thorben
BTW: I'm not THAT experienced with C++ and I'm opened up for comments about my style, but that should not be the main problem...
I'm trying to encalpsulate alsa inside a class.
The problem is when i try to use :
snd_pcm_hw_params_get_period_size(this->__params, &period_size, NULL);
or
snd_pcm_hw_params_get_period_time(this->__params, &time_period, NULL);
inside void alsa_control::record_to_file(std::string filename, int duration_in_us);
in the cpp it send 0 in period and seems to be unmodified for period_time
The .h
#ifndef ALSA_RECORDING_H
#define ALSA_RECORDING_H
#include <iostream>
#include <alsa/asoundlib.h>
#include <wav_functions.h>
#define STEREO 2
#define MONO 1
using std::cout;
using std::endl;
class alsa_control {
public:
void record_to_file(std::string filename, int duration_in_us);
alsa_control(unsigned int rate, unsigned long frames, int bits, unsigned int stereo_mode);
~alsa_control();
private:
unsigned int __rate;
unsigned int __stereo_mode;
int __bits;
snd_pcm_uframes_t __frames;
snd_pcm_hw_params_t *__params;
snd_pcm_t *__handle;
void open_pcm_device();
void set_parameters_ALSA();
alsa_control()=delete;
alsa_control(const alsa_control&)=delete;
};
#endif /* ALSA_RECORDING_H */
and the .cpp
#include <alsa_control.h>
alsa_control::alsa_control(unsigned int rate, unsigned long frames, int bits, unsigned int stereo_mode)
: __rate(rate),
__stereo_mode(stereo_mode),
__bits(bits),
__frames(frames) {
this->open_pcm_device();
snd_pcm_hw_params_alloca(&this->__params);
this->set_parameters_ALSA();
}
alsa_control::~alsa_control() {
snd_pcm_drain(this->__handle);
snd_pcm_close(this->__handle);
}
void alsa_control::open_pcm_device() {
int rc = snd_pcm_open(&this->__handle, "default", SND_PCM_STREAM_CAPTURE, 0);
if (rc < 0) {
cout << "ERROR : unable to open pcm device: " << snd_strerror(rc) << endl;
exit(1);
}
}
void alsa_control::set_parameters_ALSA() {
snd_pcm_hw_params_any(this->__handle, this->__params); // def values
snd_pcm_hw_params_set_access(this->__handle, this->__params, SND_PCM_ACCESS_RW_NONINTERLEAVED); //non interleaved
snd_pcm_hw_params_set_format(this->__handle, this->__params, SND_PCM_FORMAT_S16_LE); //16bits little-endian
snd_pcm_hw_params_set_channels(this->__handle, this->__params, this->__stereo_mode); // stereo ou mono
snd_pcm_hw_params_set_rate_near(this->__handle, this->__params, &this->__rate, NULL); // sample rate (freq echantillonage)
auto ret = snd_pcm_hw_params_set_period_size_near(this->__handle, this->__params, &this->__frames, NULL); //frames pour une période
int rc = snd_pcm_hw_params(this->__handle, this->__params);
if (rc < 0) {
cout << "ERROR - unable to set hw parameters: " << snd_strerror(rc) << endl;
exit(1);
}
}
void alsa_control::record_to_file(std::string filename, int duration_in_us) {
std::ofstream f;
int rc;
int nb_ech = 0;
snd_pcm_uframes_t period_size;
unsigned int time_period;
filename += ".wav";
f.open(filename, std::ios::binary);
write_header_wav(f, this->__rate, (short) this->__bits, (short) this->__stereo_mode, 10000); //10000 is a constant because we don't know the size of the recording
snd_pcm_hw_params_get_period_size(this->__params, &period_size, NULL);
period_size = 2048;
snd_pcm_uframes_t size = period_size * 2; /* 2 bytes/sample, 1 channels */
char *buffer = (char *) malloc(size);
snd_pcm_hw_params_get_period_time(this->__params, &time_period, NULL);
time_period = 128000;
long loops = duration_in_us / time_period;
while (loops-- > 0) {
rc = (int) snd_pcm_readi(this->__handle, buffer, period_size);
if (rc == -EPIPE) {
cout << "ERROR - overrun occurred" << endl;
snd_pcm_prepare(this->__handle);
} else if (rc < 0) {
cout << "ERROR - error from read: " << snd_strerror(rc) << endl;
} else if (rc != (int) period_size) {
cout << "ERROR - short read, read " << rc << " frames" << endl;
}
if (!(loops % 10))cout << loops << endl;
f.write(buffer, rc * 2);
nb_ech += rc;
}
f.close();
f.open(filename, std::ios::binary | std::ios::in);
write_header_wav(f, this->__rate, (short) this->__bits, (short) this->__stereo_mode, nb_ech);
f.close();
free(buffer);
}
I've just figured one way to make it works. But i'm always wondering why the functions don't works correctly.
snd_pcm_hw_params_get_period_size(this->_params, period_size, NULL); and snd_pcm_hw_params_get_period_time(this->_params, time_period, NULL); works fine when inside the set_parameters_ALSA method.
Then i've just added them to my class as private members :
snd_pcm_uframes_t _period_size; and unsigned int _time_period;
And changed the two functions to :
void alsa_control::set_parameters_ALSA() {
snd_pcm_hw_params_any(this->_handle, this->_params); // def values
snd_pcm_hw_params_set_access(this->_handle, this->_params, SND_PCM_ACCESS_RW_NONINTERLEAVED); //non interleaved
snd_pcm_hw_params_set_format(this->_handle, this->_params, SND_PCM_FORMAT_S16_LE); //16bits little-endian
snd_pcm_hw_params_set_channels(this->_handle, this->_params, this->_stereo_mode); // stereo ou mono
snd_pcm_hw_params_set_rate_near(this->_handle, this->_params, &this->_rate, NULL); // sample rate (freq echantillonage)
snd_pcm_hw_params_set_period_size_near(this->_handle, this->_params, &this->_frames, NULL); //frames pour une période
int rc = snd_pcm_hw_params(this->_handle, this->_params);
if (rc < 0) {
cout << "ERROR - unable to set hw parameters: " << snd_strerror(rc) << endl;
exit(1);
}
snd_pcm_hw_params_get_period_size(this->_params, &this->_period_size, NULL);
snd_pcm_hw_params_get_period_time(this->_params, &this->_time_period, NULL);
}
and
void alsa_control::record_to_file(std::string filename, int duration_in_us) {
std::ofstream f;
int rc;
int nb_ech = 0;
filename += ".wav";
f.open(filename, std::ios::binary);
write_header_wav(f, this->_rate, (short) this->_bits, (short) this->_stereo_mode, 10000); //10000 is an arbitrary constant because we don't know the size of the recording
snd_pcm_uframes_t size = this->_period_size * 2; /* 2 bytes/sample, 1 channels */
char *buffer = (char *) malloc(size);
long loops = duration_in_us / this->_time_period;
while (loops-- > 0) {
rc = (int) snd_pcm_readi(this->_handle, buffer, this->_period_size);
if (rc == -EPIPE) {
cout << "ERROR - overrun occurred" << endl;
snd_pcm_prepare(this->_handle);
} else if (rc < 0) {
cout << "ERROR - error from read: " << snd_strerror(rc) << endl;
} else if (rc != (int) this->_period_size) {
cout << "ERROR - short read, read " << rc << " frames" << endl;
}
f.write(buffer, rc * 2);
nb_ech += rc;
}
f.close();
f.open(filename, std::ios::binary | std::ios::in);
write_header_wav(f, this->_rate, (short) this->_bits, (short) this->_stereo_mode, nb_ech);
f.close();
free(buffer);
}
EDIT :
This problem seems to be related to driver.
Using : Advanced Linux Sound Architecture Driver Version k3.16.0-31-generic don't work with the question version
Using : Advanced Linux Sound Architecture Driver Version k3.18.8+ works good
Well, i want to learn about hooking, but it seems that the tutorials found on the internet won't run.
What i want to do is a jump hook, in C++.
Here's the code :
void DoHook(DWORD* Address, DWORD* Hook, DWORD pid){
HANDLE Server = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ , false, pid );
Address = (DWORD*)Address + 0x18;
DWORD OldProt;
DWORD HookOffset = (DWORD*)Hook-(DWORD*)Address-5;
std::wcout << "Hook on address" << std::hex << Address<< std::endl;
std::wcout << "Hook offset is " << std::hex << HookOffset << std::endl;
if ( ! VirtualProtectEx(Server, (LPVOID) Address, 40,PAGE_EXECUTE_READWRITE, &OldProt) ) {
ErrorExit(L"VirtualProtectEx");
};
char* CharPointer = (char*) Address;
BYTE newdata[5]={0xE9};
BYTE x;
int i = 1;
while ( HookOffset > 0 ) {
x = HookOffset & 0xff;
newdata[5-i] = x;
i++;
HookOffset >>= 8;
}
std::wcout << "Bytes " <<newdata[0] << " " << newdata[1] << " " << newdata[2] << " " << newdata[3] << " " << newdata[4] << std::endl;
DWORD newdatasize = sizeof(newdata);
if ( ! WriteProcessMemory(Server,Address,(LPCVOID*)newdata,newdatasize,NULL) ) {
ErrorExit(L"WriteProcessMemory");
}
// VirtualProtect((void*) Address, 40, 0x40, &OldProt);
return;
}
Here's some output text :
Process ID is 2764 // PID of the app that's being hooked
Function address is 00A81190 // this is the function i'm doing the jump to
Entry point is 00080000 // for the app that's being hooked
Hook on address 00080060 // for the app that's being hooked
Hook offset is 28048e // HookAddress - FunctionAddress - 5
Bytes e9 0 28 4 8e // this is the jump i'm planning to do
Press any key to continue . . .
However, the application doesn't update.
You must run your program as administrator to have correct permissions to write to process memory. Here is my x86 detour function which I have tested and used many times
bool Detour32(char* src, char* dst, const intptr_t len)
{
if (len < 5) return false;
DWORD curProtection;
VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &curProtection);
intptr_t relativeAddress = (intptr_t)(dst - (intptr_t)src) - 5;
*src = (char)'\xE9';
*(intptr_t*)((intptr_t)src + 1) = relativeAddress;
VirtualProtect(src, len, curProtection, &curProtection);
return true;
}
src is the address you want to place the hook, dst is the address where you want to jump to. len is the number of bytes you are going to destroy with your jmp. The jmp is 5 bytes so if you're destroying instructions which are more than 5 bytes, you need copy more than 5 of the "stolen bytes" into your destination to ensure they get executed.