Polling uBlox MAX-6Q - c

I'm currently working on a HAB (High Altitude Balloon) project, and so far, all has gone swimmingly.
I'm using an Arduino uno R3 as my main controller for this project, however, I'm struggling with the GPS side of things here.
Follwing the fantastic tutorial at UK-HAS (http://ukhas.org.uk/guides:ublox6) I'm currently working with the uBlox MAX-6Q GPS module.
I've played around with the code supplied on the tutorial, and am in the process of testing with the following sketch:
#include <SoftwareSerial.h>
SoftwareSerial GPS(3,2);
byte gps_set_success = 0;
void setup()
{
Serial.begin(9600);
GPS.begin(9600);
GPS.print("$PUBX,41,1,0007,0003,4800,0*13\r\n");
GPS.begin(4800);
GPS.flush();
uint8_t setNav[] = {
0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00,
0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC };
/*while(!gps_set_success)
{
sendUBX(setNav, sizeof(setNav)/sizeof(uint8_t));
gps_set_success = getUBX_ACK(setNav);
}*/
gps_set_success = 0;
GPS.println("$PUBX,40,GGA,0,0,0,0*5A");
GPS.println("$PUBX,40,GSA,0,0,0,0*4E");
GPS.println("$PUBX,40,RMC,0,0,0,0*47");
GPS.println("$PUBX,40,GSV,0,0,0,0*59");
GPS.println("$PUBX,40,VTG,0,0,0,0*5E");
GPS.println("$PUBX,40,GLL,0,0,0,0*5C");
}
void loop()
{
printGPSData();
delay(3000);
}
void printGPSData()
{
Serial.println("Polling: $PUBX,00*33");
GPS.println("$PUBX,00*33");
while (GPS.available())
{
if (GPS.available() > 0)
{
char c = GPS.read();
Serial.write(c);
}
}
Serial.println();
}
void sendUBX(uint8_t *MSG, uint8_t len)
{
for (int i = 0; i<len; i++)
{
GPS.write(MSG[i]);
Serial.print(MSG[i], HEX);
}
GPS.println();
}
boolean getUBX_ACK(uint8_t *MSG)
{
uint8_t b;
uint8_t ackByteID = 0;
uint8_t ackPacket[10];
unsigned long startTime = millis();
Serial.print(" * Reading ACK response: ");
ackPacket[0] = 0xB5;
ackPacket[1] = 0x62;
ackPacket[2] = 0x05;
ackPacket[3] = 0x01;
ackPacket[4] = 0x02;
ackPacket[5] = 0x00;
ackPacket[6] = MSG[2];
ackPacket[7] = MSG[3];
ackPacket[8] = 0;
ackPacket[9] = 0;
for (uint8_t i=2; i<8; i++)
{
ackPacket[8] = ackPacket[8] + ackPacket[i];
ackPacket[9] = ackPacket[9] + ackPacket[8];
}
while(1)
{
if (ackByteID > 9)
{
Serial.println("(SUCCESS!)");
return true;
}
if (millis() - startTime > 3000)
{
Serial.println("(FAILED!)");
return false;
}
if (GPS.available())
{
b = GPS.read();
if (b == ackPacket[ackByteID])
{
ackByteID++;
Serial.print(b,HEX);
}
else
{
ackByteID = 0;
}
}
}
}
I've been struggling with 2 issues here. I'm hitting the timeout in the getUBX_ACK method, thus meaning, no response from the GPS module to confirm the setting change. This isn't currently crucial as if need be, I can configure this through uCenter, and add a battery to preserve the settings.
However, the main issue I am facing is when I run this sketch with the setNav block in the setup commented out, I am able to turn off all the automatic NMEA sentences, and poll the module, but upon polling, I am greeted with the following:
Polling: $PUBX,00*33
followed by a number of invalid characters. (Rectangles)
I've checked baud rates stop bits, parity etc. along with the actual data line connections, but cannot seem to find the source of this. It suggests bad data lines/encoding/ to me, but I was wondering if this was something anybody else had ever experienced?
EDIT:
So, after more testing, it would appear that the SoftwareSerial library is NOT at fault here. I tried first, writing to an OpenLog using the SoftwareSerial GPS ipput, and then by hooking the GPS Tx directly into the OpenLog Rx. Same story. Opening the resulting file in Notepad++ yeilds a lovely bunch of 'NULL' characters. I can only presume something in the way the GPS is configured at runtime is causing a problem.
Bypassing the Arduino, and turning off the sentences and polling the GPS directly through serial works with no problems, the data is returned perfectly.

So, I'm feeling a bit daft here, there is an issue, but not with the GPS, I'll ask as a seperate question. It would appear I'm having problems with the SoftwareSerial library.
The SoftwareSerial library cannot use multiple soft serial ports at the same time, it would appear that despite many suggestions otherwise, I cannot switch between sof serial ports, as I am unable to begin() the second port, it would appear that the port first declared is to be unusable.

Related

unsigned char array breaks at zero bytes in Azure Sphere SDK when passing to a function

I am using (1)Azure Sphere MT3620 Starter Kit and (2)RS485 CLICK 5V and (3)Geovan Board
Here is the setup (I don't have latest picture, so #2 is swapped to first slot in the MT3620)
My MT3620 is registered all good with Azure IoTHub and pushing all telemetry data to cloud. Now I am trying to sent some HEX commands from MT3620 to Geovan Board via RS485 CLICK 5V
Here is my Calling code
//RS485 to Geovan to Update Registry values
unsigned char updateAllChannelData[] = { 0x01, 0x06, 0x00, 0x19, 0x00, 0x01, 0x99, 0xCD };
SendUartMessage(uartFd, updateAllChannelData, sizeof(updateAllChannelData));
delay(10);
//RS485 to Geovan to Read all channels
unsigned char readAllChannel[] = { 0x01, 0x03, 0x00, 0x01, 0x00, 0x10, 0x15, 0xC6 };
SendUartMessage(uartFd, readAllChannel, sizeof(readAllChannel));
delay(1.5); //20
when data is passed to SendUartMessage both arrays (updateAllChannelData and readAllChannel) breaks at 0x00 so final command being passed to Geovan Board is incomplete and no response is received.
Here is SendUartMessage method
static void SendUartMessage(int uartFd, const char* dataToSend, size_t totalBytesToSend)
{
// dataToSend breaks at 0x00 right here not after executing any lines below
// First command array becomes {0x01, 0x06}
// Second command array becomes {0x01, 0x03}
size_t totalBytesSent = 0;
int sendIterations = 0;
close(r1PinFd);
r1PinFd = GPIO_OpenAsOutput(MIKROE_PWM, GPIO_OutputMode_PushPull, GPIO_Value_High);
while (totalBytesSent < totalBytesToSend) {
sendIterations++;
// Send as much of the remaining data as possible
size_t bytesLeftToSend = totalBytesToSend - totalBytesSent;
const char* remainingMessageToSend = dataToSend + totalBytesSent;
ssize_t bytesSent = write(uartFd, remainingMessageToSend, bytesLeftToSend);
if (bytesSent == -1) {
Log_Debug("ERROR: Could not write to UART: %s (%d).\n", strerror(errno), errno);
exitCode = ExitCode_SendMessage_Write;
return;
}
totalBytesSent += (size_t)bytesSent;
}
int c, d;
sleep(5);
close(r1PinFd);
r1PinFd = GPIO_OpenAsOutput(MIKROE_PWM, GPIO_OutputMode_PushPull, GPIO_Value_Low);
Log_Debug("Sent %zu bytes over UART in %d calls.\n", totalBytesSent, sendIterations);
}
FYI: I have tested these commands as below
Connect Geovan directly to PC
using Hercules SETUP utility open com port on which Geovan is connected
send HEX command "0x01, 0x06, 0x00, 0x19, 0x00, 0x01, 0x99, 0xCD" wait 10 seconds
send HEX command "0x01, 0x03, 0x00, 0x01, 0x00, 0x10, 0x15, 0xC6" wait 10 seconds
received expected response
This confirms from Geovan these commands as sent and responded correctly. So the challenge is sending same commands from MT3620 through RS485 CLICK 5V to Geovan Board.
Can someone point out the what am I missing over here?
Update:
Before stepping in to function, this is how my array looks
After stepping in to the function, this is what it looks like

Very slow SPI writing STM32

I am currently writing a code to write on an LCD screen pixel by pixel. The code works fine, however the speed at which the code is processed is incredibly slow. The goal is simply to write number on the LCD screen so I am using the "switch" function with a "for loop" to read each of the bit I will activate. I am wondering if someone could tell me a way to speed up my code...
int* switch_library_number_1(int num, int octet)
{
switch(num)
{
case 0 : ;
int number_0 [] = {0x80, 0x08,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x88,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, ...};
int * pNumber_0 = &number_0[octet];
return pNumber_0;
break;
case 1 : ;
int number_1 [] = {0x80, 0x08,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, ...};
int * pNumber_1 = &number_1[octet];
return pNumber_1;
break;
}
Then it goes up to nine like that, I don't think you need to seem all the cases. Plus even if I deleted most of them, I have 522 bytes by number. The rest of the code goes as fallow :
int main(void)
{
ADC_Initialization();
SPI_Initialization();
int nombre_octet = 522;
int premier_nombre;
int deuxieme_nombre;
while(1)
{
GPIOA->BSRRL = CS;
for(int i = 0; i < nombre_octet; i++)
{
write_spi(*switch_library_number_1(0, i));
}
GPIOA -> BSRRH = CS;
for(int i = 0; i < 100; i++)
{
}
GPIOA->BSRRL = CS;
for(int i = 0; i < nombre_octet; i++)
{
write_spi(*switch_library_number_2(1, i));
}
GPIOA -> BSRRH = CS;
}
}
Finally, here is the write_SPI function, but due to it's simplicity, I don't think that it is the problem.
void write_spi(char data)
{
SPI1->DR = data;
while (!(SPI1->SR & SPI_I2S_FLAG_TXE));
while (!(SPI1->SR & SPI_I2S_FLAG_RXNE));
while (SPI1->SR & SPI_I2S_FLAG_BSY);
}
Thanks in advance!
I quite like the way you split your code into three snippets. I can suggest improvements for each of them:
switch_library_number_1():
This could be just a 2D array, number[][], or if number_0, number_1... are not of the same length, it could be an array of pointers to these. There would need to be checks for valid num and offset. This might be a minor speed improvement.
Your number_0... arrays are currently on stack, and read-write. Make them const, so they won't use RAM.
Currently you are returning a pointer to memory location on stack - this doesn't normally work, if it does it's by luck and accident. You should not access stack data when you're out of scope (function) where it's been defined. static const would make this safe, as it wouldn't be on stack anymore.
main loop:
It's a bit odd to call switch_library_number_1/2 on each loop iteration. You know your data will just be in array. This could probably be replaced by write_spi(number[0][i]); if number array is properly set up. This should get you some speed improvement, as it very much simplifies data fetching.
You appear to have a busy loop. That's a tricky practice (I bet 100 is a guess, and note that compiler could optimise this loop away). If possibly use some library provided delay function or a timer to get precise delays. Is this an actual requirement of SPI slave?
write_spi(char data):
char should be unsigned char here. chars might be signed or unsigned, so when you're using them as bytes (not actual string characters), you should specify signedness.
You seem to wait for every byte transmission to finish, which is safe, but a bit slow. Normally this can be rewritten into a faster alternative of wait_for_SPI_ready_for_TX; SPI_TX, where you only wait before sending next byte. Note that you will also need to wait for byte to be transmitted fully before pulling CS back high again. This could be a big speed improvement.
Some other things to consider:
What's the actual SPI clock? There may be huge speed improvements if clock is increased.
How did you measure this to be "slow"? Does it point to slow parts of code (what are those then? If not obvious from C, what are they assembled to?)
Have you got an oscilloscope/logic analyser to look at actual signals on wire? This may provide useful data.
I had a similar problem with STM32F207 Series Cortex-M3 controller, when I observed the TX line through Oscillator, I saw that CHIP_SELECT disable was taking too much time to set in, after all the data has sent.I figured out it is related to flag controls So ı play with the control flags a little bit, Here how it worked out just fine for me;
static void SPI_Send(uint16_t len,uint8_t* data)
{
uint16_t i;
for(i = 0;i<len;i++)
{
SPI_I2S_SendData(SPI1,*(data+i));
while(!(SPI1->SR & SPI_SR_TXE));
}
while(SPI1->SR & SPI_SR_BSY);
CHIP_SEL_DISABLE;
}
I believe it is slow because you are also checking the 'Receive Buffer Not Empty' where you don't need to.

why does this make my display go mad? (but only sometimes)

it seems this code make my display go crazy sometimes (but only sometimes). But when I remove dat=~dat; it seems to work fine.
why?
what I am trying to do here is just make the ascii letters be the oposite: so for example:
11001000 will be:
00110111
or
10101111 would be:
01010000
the reason for doing this is that i want to have one row (the active row) in the diplay window with black on white pixels instead of opostie like the rest of the display window.
Is there some other way I could do this? (invert the numbers)
FYI: I am programing in C. Atmel studio. atmega 4809, SSD1305z display, SPI-simular interface.
void displayinvertedString(char str[], uint8_t ypos,uint8_t xpos)
{
Set_Page_Address(ypos);
Set_Column_Address(xpos);
int len = strlen(str);
uint8_t dat;
int temp;
for (int e=0; e<len; e++)
{
dat = 0xff;
Write_Data(dat); //to get an extra space between the
// numbers/letters for
//making it easier to read the text on the display
temp = str[e];
temp=temp-0x20; // As the lookup table starts from Space(0x20)
for (int w=0; w<5; w++)
{
dat= OledFontTable[temp][w]; // Get the data to be displayed for LookUptable
dat =~ dat;
Write_Data(dat);
}
}
}
----------
static uint8_t OledFontTable[][FONT_SIZE]={
//static uint8_t OledFontTable[] = {
0x00, 0x00, 0x00, 0x00, 0x00, // space
0x00, 0x00, 0x2f, 0x00, 0x00, // !
0x00, 0x07, 0x00, 0x07, 0x00, // "
0x14, 0x7f, 0x14, 0x7f, 0x14, // #
0x24, 0x2a, 0x7f, 0x2a, 0x12, // $
0x23, 0x13, 0x08, 0x64, 0x62, // %
0x36, 0x49, 0x55, 0x22, 0x50, // &
ETC. Etc.
just more raw pixel data here. this part ends like this:
0x00, 0x00, 0xFF, 0x00, 0x00, // |
0x00, 0x82, 0x7C, 0x10, 0x00, // }
0x00, 0x06, 0x09, 0x09, 0x06 // ~ (Degrees)
};
void Write_Data(unsigned char Data)
{
PORTA.OUTCLR = PIN7_bm; //cs
PORTB.OUTSET = PIN2_bm; //dc
Write_Command(Data); //
}
void Write_Command(unsigned char data)
{
SPI0.DATA = data; // copy data to DATA register
while ((SPI0.INTFLAGS & SPI_RXCIF_bm) == 0) ; //wait
}
I have asked a bit about this before. but i thought i would look "cleaner" with a new tread since info was missing from the last one.
It turned out I needed to toggle the chip select (CS) so the clock did not get out of sync with time.
The clock sync drifted with time.
It was going crazy faster for the non inverted data for some reason. But with the normal data it happend after some time also.
Thank you for the answers.

Having timer collision issue with using Servo and SoftwareSerial

I'm having timer collision problems with using Servo.h and SoftwareSerial.h on Arduino Nano board. And now I need 2 pairs of Serial pins by using NFC Module and Arduino's Serial Monitor on my laptop.
If the information I got is not wrong, there are three timers (timer0, timer1, timer2) available in Nano board. As I heard timer1 is 16bit timer, and both Servo.h and SoftwareSerial.h use that timer1 at the same time on Nano board that's why they can't avoid timer collision issue.
Yet I need to use both header files without timer collision. In this case, what should I do? Do I have to modify Servo.h file for not using timer1?
Because all I do with my Servo Motor is controlling angular position.
Therefore, using 16bit-timer is of no use in this project I'm proceeding unless I use PWM control.
So, at this point, I want to use timer0 or timer2 (both are 8 bit-timer) instead of using timer1. If not, the timer1 from both header files of Servo and Software will be collide.
Following is the source code I use.
const unsigned char wake[24]={
0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xfd, 0xd4, 0x14, 0x01, 0x17, 0x00};//wake up NFC module
const unsigned char firmware[9]={
0x00, 0x00, 0xFF, 0x02, 0xFE, 0xD4, 0x02, 0x2A, 0x00};//
const unsigned char tag[11]={
0x00, 0x00, 0xFF, 0x04, 0xFC, 0xD4, 0x4A, 0x01, 0x00, 0xE1, 0x00};//detecting tag command
const unsigned char std_ACK[25] = {
0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x0C,
0xF4, 0xD5, 0x4B, 0x01, 0x01, 0x00, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x00};
unsigned char old_id[5];
unsigned char receive_ACK[25];//Command receiving buffer
//int inByte = 0; //incoming serial byte buffer
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#define print1Byte(args) mySerial.write(args)
#define print1lnByte(args) mySerial.write(args),mySerial.println()
#else
#include "WProgram.h"
#define print1Byte(args) mySerial.print(args,BYTE)
#define print1lnByte(args) mySerial.println(args,BYTE)
#endif
#include <Servo.h>
#include <NeoSWSerial.h>
NeoSWSerial mySerial(5,6);
volatile uint32_t newlines = 0UL;
Servo sv;
int pos1=0; //initial value = 93 degree
int pos2=180;
int sw1 = 4;
static void handleRxChar( uint8_t c )
{
if (c == '\n')
newlines++;
}
void setup(){
mySerial.attachInterrupt( handleRxChar );
pinMode(sw1, INPUT_PULLUP);
sv.attach(9);
Serial.begin(9600); // open serial with PC
mySerial.begin(9600); //open serial1 with device
//Serial2.begin(115200);
wake_card();
delay(100);
read_ACK(15);
delay(100);
display(15);
}
void loop(){
send_tag();
read_ACK(25);
delay(100);
if (!cmp_id ()) { //nfc tag
if (test_ACK ()) {
display (25);
sv.write(pos1);
delay(2500);
sv.write(pos2);
}
}
else if (cmp_id()){ // switch
if(digitalRead(sw1) == LOW){
sv.write(pos1); // waits 15ms for the servo to reach the position
}
else if(digitalRead(sw1) == HIGH){
sv.write(pos2);
}
}
copy_id ();
}
void copy_id (void) {//save old id
int ai, oi;
for (oi=0, ai=19; oi<5; oi++,ai++) {
old_id[oi] = receive_ACK[ai];
}
}
char cmp_id (void){//return true if find id is old
int ai, oi;
for (oi=0,ai=19; oi<5; oi++,ai++) {
if (old_id[oi] != receive_ACK[ai])
return 0;
}
return 1;
}
int test_ACK (void) {// return true if receive_ACK accord with std_ACK
int i;
for (i=0; i<19; i++) {
if (receive_ACK[i] != std_ACK[i])
return 0;
}
return 1;
}
void send_id (void) {//send id to PC
int i;
Serial.print ("ID: ");
for (i=19; i<= 23; i++) {
Serial.print (receive_ACK[i], HEX);
Serial.print (" ");
}
Serial.println ();
}
void UART1_Send_Byte(unsigned char command_data){//send byte to device
print1Byte(command_data);
#if defined(ARDUINO) && ARDUINO >= 100
mySerial.flush();// complete the transmission of outgoing serial data
#endif
}
void UART_Send_Byte(unsigned char command_data){//send byte to PC
Serial.print(command_data,HEX);
Serial.print(" ");
}
void read_ACK(unsigned char temp){//read ACK into reveive_ACK[]
unsigned char i;
for(i=0;i<temp;i++) {
receive_ACK[i]= mySerial.read();
}
}
void wake_card(void){//send wake[] to device
unsigned char i;
for(i=0;i<24;i++) //send command
UART1_Send_Byte(wake[i]);
}
void firmware_version(void){//send fireware[] to device
unsigned char i;
for(i=0;i<9;i++) //send command
UART1_Send_Byte(firmware[i]);
}
void send_tag(void){//send tag[] to device
unsigned char i;
for(i=0;i<11;i++) //send command
UART1_Send_Byte(tag[i]);
}
void display(unsigned char tem){//send receive_ACK[] to PC
unsigned char i;
for(i=0;i<tem;i++) //send command
UART_Send_Byte(receive_ACK[i]);
Serial.println();
}
Summary
I am having timer collision issue with using Servo.h and SoftwareSerial.h.
they both share timer1 at the same time. To avoid this collision issue and make these two work fine, what should I do? Should I do something with the source code like by adding up few lines of code or modify those header files?
Normally, I would have suggested AltSoftSerial as the alternative to SoftwareSerial (read more here), but it also conflicts with the Servo library's TIMER1 use. It can only be used on two specific pins.
I think my NeoSWSerial would do the trick. It re-uses the micros() clock (TIMER0) and Pin Change interrupts to implement a software serial port. This limits it to bauds rates 9600, 19200 and 38400, but it is much more efficient than SoftwareSerial. It can be used on any two pins.
Update
I would not suggest using a software serial port at 115200, as it can be unreliable above 38400. You might be able to send a baud rate configuration command to the NFC module to set it to a lower rate.
BTW, if you are sending information (not just receiving), all software serial port libraries disable interrupts during transmission, except AltSoftSerial... which you can't use. Just be aware of that, because it may affect your Servo when you transmit on NeoSWSerial.
Also, be sure you are using one of the PWM pins for the servo. If the Servo library is creating the PWM signal with software (just like a software serial port), the CPU won't have time for much else.
It might be better to put the NFC module on the hardware serial port, Serial. For debug prints, use NeoSWSerial connected to a TTL Serial-to-USB converter. Then open the Serial Monitor on that converter's COM port. Remove the debug later, because transmitting disables interrupts.
There are other boards that have additional UARTS. For example, an Arduino Leo (ATMega32U4 MCU) has an extra serial port, Serial1, that you could use for the NFC. Serial would still be available for debug prints.

C - Writing structs to a file (.pcap)

I am trying to write a .pcap file, which is something that can be used in Wireshark.
In order to do that, I have a couple of structs with various data types I need to write to a file. (see code)
So, I create the struct instances, fill in the data, use FILE* fp = fopen("test.pcap","w"), and then I'm unsure how to properly write it to the file. I believe I should use memcpy but I'm not sure of the best way to do it. I have mostly resorted to C++ libraries in the past to do this. Any suggestions?
typedef struct pcap_hdr_s {
uint32_t magic_number; /* magic number */
uint16_t version_major; /* major version number */
uint16_t version_minor; /* minor version number */
int32_t thiszone; /* GMT to local correction */
uint32_t sigfigs; /* accuracy of timestamps */
uint32_t snaplen; /* max length of captured packets, in octets */
uint32_t network; /* data link type */
} pcap_hdr_t;
typedef struct pcaprec_hdr_s {
uint32_t ts_sec; /* timestamp seconds */
uint32_t ts_usec; /* timestamp microseconds */
uint32_t incl_len; /* number of octets of packet saved in file */
uint32_t orig_len; /* actual length of packet */
} pcaprec_hdr_t;
typedef struct ethernet_hdr_s {
uint8_t dst[6]; /* destination host address */
uint8_t src[6]; /* source host address */
uint16_t type; /* IP? ARP? RARP? etc */
} ethernet_hdr_t;
typedef struct ip_hdr_s {
uint8_t ip_hl:4, /* both fields are 4 bits */
ip_v:4;
uint8_t ip_tos;
uint16_t ip_len;
uint16_t ip_id;
uint16_t ip_off;
uint8_t ip_ttl;
uint8_t ip_p;
uint16_t ip_sum;
uint32_t ip_src;
uint32_t ip_dst;
}ip_hdr_t;
typedef struct udp_header
{
uint16_t src;
uint16_t dst;
uint16_t length;
uint16_t checksum;
} udp_header_t;
Use libpcap or WinPcap - pcap_open_dead() to get a "fake" pcap_t to use with pcap_dump_open() to specify the link-layer header type (for Ethernet, use DLT_EN10MB) and snapshot length (use 65535), pcap_dump_open() to open the file for writing, pcap_dump() to write out a packet, and pcap_dump_close() to close the file. MUCH easier than directly using fopen(), fwrite(), and fclose() (which are what libpcap/WinPcap use "under the hood").
And, yes, you have to get the byte order in the packets correct. The byte order depends on the protocol; for the type field in the Ethernet header, and for all multi-byte fields in IP, TCP, and UDP headers, they have to be in big-endian order. (The magic number in the pcap file is irrelevant to this - it only indicates the byte order of the fields in the file header and the per-packet record header, NOT the byte order of the fields in the packet, as well as, due to the way it's implemented in Linux, the meta-data at the beginning of packets in Linux USB captures. The packet data is supposed to look exactly as it would "on the wire".)
Use fwrite(). You need to check this info but I think .pcap files are written in binary mode.
Example:
pcaprec_hdr_t pcaprec_hdr;
// fill pcaprec_hdr with valid info
FILE* pFile = NULL;
pFile = fopen ("myfile.pcap" , "wb"); // open for writing in binary mode
fwrite (&pcaprec_hdr, 1, sizeof(pcaprec_hdr_t) , pFile);
fclose(pFile);
Here's my understanding of what Guy Harris is suggesting. So, as per Kyslik's request, we have:
#include <libpcap/pcap.h>
/* Ethernet/IP/SCTP INIT chunk */
static const unsigned char pkt1[82] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ........ */
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, /* ......E. */
0x00, 0x44, 0x55, 0xb1, 0x00, 0x00, 0x40, 0x84, /* .DU...#. */
0x26, 0x83, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, /* &....... */
0x00, 0x01, 0x00, 0x01, 0x1f, 0x90, 0x00, 0x00, /* ........ */
0x00, 0x00, 0x68, 0xe5, 0x88, 0x1b, 0x01, 0x00, /* ..h..... */
0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, /* .$...... */
0xa0, 0x00, 0x00, 0x04, 0xff, 0xff, 0x00, 0x00, /* ........ */
0x16, 0x2e, 0x80, 0x00, 0x00, 0x04, 0xc0, 0x00, /* ........ */
0x00, 0x04, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x05, /* ........ */
0x00, 0x00 /* .. */
};
int main(int argc, char *argv[]) {
pcap_t *handle = pcap_open_dead(DLT_EN10MB, 1 << 16);
pcap_dumper_t *dumper = pcap_dump_open(handle, "/tmp/pktcap/cap.pcap");
struct pcap_pkthdr pcap_hdr;
pcap_hdr.caplen = sizeof(pkt1);
pcap_hdr.len = pcap_hdr.caplen;
pcap_dump((u_char *)dumper, &pcap_hdr, pkt1);
pcap_dump_close(dumper);
return 0;
}

Resources