Reading binary files and structs are a new area for me.
I understand how to read in the file and attempted various methods to read the raw data but seems I need to use struct.
I am trying to translate these instructions to python code:
The beginning of the Binary Merge file contains an array of GWI_file_header_struct structs (defined in file INET_INT.H) for the various channels, followed by the interlaced 32bit floating point data. The 1st 4 bytes in the header is the length of the header for 1 channel in bytes (i.e. 516 = 0x0204). To read the # of channels stored in the file, read the 'channelsPerFile' field of the 1st struct (e.g. to see how many headers are there). After the header, the data is saved in an interlaced form, where points are stored in the order that they are acquired in time.
The main confusion is how do I translate this to:
struct.unpack(...)
INET_INT.H struct:
typedef struct GWI_file_header_struct{ // This struct is at the beginning of GWI iNet BINARY files that contain waves.
//
// Macintosh:
//
// file type: 'GWID'
// creator type: 'ioNe' NETWORK_DATA_CREATOR
// ----------------------------------
// HEADER INFORMATION
iNetINT32 headerSizeInBytes; // contains length, in bytes, of this header (this does not include any data) { bytes 0..3, base 0 }
// ----------------------------------
// FILE INFORMATION
iNetINT32 int32key; // 32bit key that should contain 0x12345678 (this will help you make sure your byte lanes are ok).
// { bytes 4..7, base 0 }
iNetINT32 file_endian; // endian mode of stored data on disk: 0 = bigEndian_ion, 1 = littleEndian_ion
// { bytes 8..11, base 0 }
iNetINT16 int16key; // 16bit key that should contain 0x55b4; (this field should consume 2 bytes
// in the struct -- no padding) (i.e. INET_INT16_KEY = 0x55b4)
// { bytes 12..13, base 0 }
iNetINT16 zero; // set to 0 (this field should consume 2 bytes in the struct -- no padding)
// { bytes 14..15, base 0 }
// # of seconds since Jan 1, Midnight, 1904 that the acquisition started (this is used to compute the
// date of acquisition). This overflows in 2030.
// Strip Chart: 1st digitized point in entire stream (i.e. 1st pt of 1st scan)
// Osc Mode: 1st point in current scan, secsSince1904_Int64 units
// { bytes 16..19, base 0 }
iNetUINT32 acquisition_SecsSince1904_FixedUint32_OverflowIn2030;
// ----------------------------------
// # OF POINTS STORED
//
// This file contains a set of scans. Each scan is 1 to .5billion points long. For example,
// we might have 100 scans, each 1000 points long. In this example:
//
// pointsPerScanThisChannel_LSW = 1000
// pointsPerScanThisChannel_MSW = 0
//
// numScansStoredBeforeLastScan = 99
//
// numPointsInLastPartialScan_LSW = 1000
// numPointsInLastPartialScan_MSW = 0
//
// Each channel can have a different number of points per scan due to the sampleRateChanMULTiplier
iNetUINT32 pointsPerScanThisChannel_LSW;
iNetUINT32 pointsPerScanThisChannel_MSW;
// # points per scan = (pointsPerScanThisChannel_MSW * 2^32) + pointsPerScanThisChannel_LSW
// { bytes 20..23, base 0 }
// { bytes 24..27, base 0 }
iNetUINT32 numScansStoredBeforeLastScan_LSW;
// # of complete scans stored in file
// { bytes 28..31, base 0 }
// iNetUINT32 numScansStoredBeforeLastScan_MSW;
// this is defined below, at the end of the struct
iNetUINT32 numPointsInLastPartialScan_LSW;
iNetUINT32 numPointsInLastPartialScan_MSW;
// # points stored in last scan if it is partially complete = (numPointsInLastPartialScan_MSW * 2^32) + numPointsInLastPartialScan_LSW
// { bytes 32..35, base 0 }
// { bytes 36..39, base 0 }
// ----------------------------------
// TIME INFORMATION
iNetFLT32 firstPoint_Time_Secs; // time of 1st point, units are seconds
// { bytes 40..43, base 0 }
iNetFLT32 endUser_channel_samplePeriod_Secs;
// time between points for this channel,
// units are seconds. Notice that channels
// can have different sample rates, which
// is the master_endUser_SampleRate / sampleRate_Divider,
// where 'sampleRate_Divider' is an integer.
// { bytes 44..47, base 0 }
// ----------------------------------
// TYPE OF DATA STORED
iNetINT32 arrayDataType; // Type of src array data. iNetDataType:
//
// 0 iNetDT_INT16: 16bit integer, signed
// 2 iNetDT_UINT16: 16bit integer, unsigned
// 3 iNetDT_INT32: 32bit integer, signed
// 4 iNetDT_UINT32: 32bit integer, unsigned
// 5 iNetDT_FLT32: 32bit float (IEEE flt32 format)
// 6 iNetDT_Double: 'double', as determined by the compiler
// (e.g. flt64, flt80, flt96, flt128)
// see 'bytesPerDataPoint' field to see
// how many bytes
// { bytes 48..51, base 0 }
iNetINT32 bytesPerDataPoint; // # of bytes for each datapoint (e.g. 4 for 32bit signed integer)
// { bytes 52..55, base 0 }
iNetStr31 verticalUnitsLabel; // pascal string of vertical units label (e.g. "Volts")
// { bytes 56..87, base 0 }
iNetStr31 horizontalUnitsLabel; // horizontal units label, e.g. "Secs", pascal string (0th char is the # of valid chars)
// { bytes 88..119, base 0 }
iNetStr31 userName; // user named set by user, e.g. "Pressure 1" , pascal string (0th char is the # of valid chars)
// { bytes 120..151, base 0 }
iNetStr31 chanName; // name of channel, e.g. "Ch1 Vin+", pascal string (0th char is the # of valid chars)
// { bytes 152..183, base 0 }
// ----------------------------------
// DATA MAPPING
//
iNetINT32 minCode; // if data is stored in integer format, this contains the mapping from integer
iNetINT32 maxCode; // to engineering units (e.g. +/-2048 A/D data is mapped to +/- 10V, minCode = -2048,
iNetFLT32 minEU; // maxCode = +2047, minEU = -10.000, maxEU = +9.995.
iNetFLT32 maxEU; //
// { bytes 184..187, base 0 }
// { bytes 188..191, base 0 }
// { bytes 192..195, base 0 }
// { bytes 196..199, base 0 }
// ----------------------------------
// iNet NETWORK ADDRESS (this does not need
// to be filled in, 0L's are ok)
iNetINT32 netNum; // channel network # (this pertains to iNet only; use 0 otherwise)
// { bytes 200..203, base 0 }
iNetINT32 devNum; // channel device # (this pertains to iNet only; use 0 otherwise)
// { bytes 204..207, base 0 }
iNetINT32 modNum; // channel module # (this pertains to iNet only; use 0 otherwise)
// { bytes 208..211, base 0 }
iNetINT32 chNum; // channel channel # (this pertains to iNet only; use 0 otherwise)
// { bytes 212..215, base 0 }
// ----------------------------------
// END USER NOTES
iNetStr255 notes; // pascal string that contains notes about the data stored.
// { bytes 216..471, base 0 }
// ----------------------------------
// MAPPING
iNetFLT32 /* must remain flt32 */ internal1; // Mapping from internal engineering units (e.g. Volts) to external engineering
iNetFLT32 /* must remain flt32 */ external1; // units (e.g. mmHg). This is used for 2 point linear mapping/calibration to
iNetFLT32 /* must remain flt32 */ internal2; // a new, user defined, coordinate system. instruNet World does not read these values
iNetFLT32 /* must remain flt32 */ external2; // from the wave files, yet instead reads them from the instrNet.prf file -- they
// are only stored for the benefit of other software that might read this file. gsw 12/1/96
// { bytes 472..475, base 0 }
// { bytes 476..479, base 0 }
// { bytes 480..483, base 0 }
// { bytes 484..487, base 0 }
iNetFLT32 flt32key; // flt32 key set to 1234.56 (i.e. INET_FLT32_KEY), Used to test floating point code. gsw 12/1/96
// { bytes 488..491, base 0 }
iNetINT32 sampleRate_Divider; // this channel is digitized at the master_endUser_SampleRate divided
// this 'sampleRate_Divider' (i.e. sampleRateChanMULT_integerRatio_N_int64)
// (helpful with FileType Binary Merge), gsw 1/29/97. Note: This field was introduced 1/29/97 and
// files saved before that time set it to 0.
// { bytes 492..495, base 0 }
iNetINT32 channelsPerFile; // # of channels per file (i.e. interlaced after array of headers) (helpful with FileType Binary Merge), gsw 1/29/97
// Note: This field was introduced 1/29/97 and files saved before that time set it to 0.
// { bytes 496..499, base 0 }
// ----------------------------------
// EXPANSION FIELDS
#if 1 // gsw 12/23/09
// # of complete scans stored in file, MS 32bits
// { bytes 500..503, base 0 }
iNetUINT32 numScansStoredBeforeLastScan_MSW;
#else
iNetINT32 expansion8; // expansion fields that are preset to
#endif
iNetINT32 expansion9; // 0 and then ignored
iNetINT32 expansion10; // { bytes 500..503, base 0 }
// { bytes 504..507, base 0 }
// { bytes 508..511, base 0 }
// ----------------------------------
// KEY TO TEST STRUCT PACKING
iNetINT32 int32key_StructTest; // 32bit key that should contain 0x12345678; (i.e. INET_INT32_KEY)
// { bytes 512..515, base 0 }
// ----------------------------------
// ACTUAL DATA
/* iNetFLT32 *data[1]; */ // contains array of data of type 'arrayDataType'
} GWI_file_header_struct;
Final Code and Results:
Code
from struct import *
# Current 3 channels: Ch11 Vin+, Ch13 Vin+ and Ch15 Vin+
# Header info extracted using provided header struct (INET_INT.H)
# After the header, the data is saved in an interlaced form,
# where points are stored in the order that they are acquired in time.
# 3 channels: A[0], B[0], C[0], A[1], B[1], C[1]...
# After header = 516 header size x 3 channels = 1,548 bytes
# Start of data at 1,548 bytes?
with open(file, "rb") as f:
byte = f.read(12)
header_size, int32key, file_endian = unpack('<3i', byte)
# channel name 1
f.seek(152)
chan = f.read(183-152)
chan = struct.unpack("<31s", chan)[0].rstrip(b'\x00').lstrip(b'\t')
# channel name 2
f.seek(152+header_size)
chan2 = f.read(183-152)
chan2 = struct.unpack("<31s", chan2)[0].rstrip(b'\x00').lstrip(b'\t')
print(header_size, int32key, file_endian)
print("channel 1: {}".format(chan))
print("channel 2: {}".format(chan2))
Results
516 305419896 1
channel 1: b'Ch11 Vin+'
channel 2: b'Ch13 Vin+'
Ok, this is not a full answer but I feel comments would be really unreadable here.
The first step is reading the first 12 bytes (three 4-bytes integers), and unpack them so we can check the endianness. Let's try big-endian first
from struct import *
with open(file, "rb") as f:
byte = f.read(12)
header_size, int32key, file_endian = unpack('>3i', byte)
We expect to have int32key set at 305419896 (= \x12345678). If we get another value then let's switch to little-endian, i.e. change our unpack format string to <3i.
At this point we can read the rest of the header, with the same logic, and get all the info we need to read data for the first channel. I hope this can be a good start for you.
Related
I am trying to explore datatypes' memory layout using GDB-Python Type API (gdb.types). Specifically, I would like to find a way to get the absolute Offsets of all members of a nested struct which is defined inside a parent struct.
In C, I have defined:
typedef struct
{
int a;
short bf1:2;
char b:4;
struct //nested struct
{
long x;
long y;
};
} a_struct;
Using ptype command in gdb, I get:
(gdb) ptype /o a_struct
/* offset | size */ type = struct a_struct {
/* 0 | 4 */ int a;
/* 4:14 | 2 */ short bf1 : 2;
/* 4: 2 | 1 */ char b : 4;
/* XXX 2-bit hole */
/* XXX 3-byte hole */
/* 8 | 16 */ struct {
/* 8 | 8 */ long x;
/* 16 | 8 */ long y;
/* total size (bytes): 16 */
};
/* total size (bytes): 24 */
}
The above output shows the offsets of the fields of the nested anonymous struct as absolute values from the beginning of the parent struct, that is x is at byte 8 and y at byte 16.
I am trying to get the same results using the GDB Python Type API but without success. In particular, I am using gdb.types.deep_items(lookup_type) method which returns the relative offsets of the fields of the nested struct, that is 0 for the first field (x) and 8 for the second field (y).
Is there any way to get 8 for x and 16 for y (as ptype output shows) using the GDB Python API?
Thank you
I can't check right now, but if I remember correctly, this plugins for gdb provides absolute offsets: https://blog.mozilla.org/sfink/2018/08/17/type-examination-in-gdb/. It produces output such as:
(gdb) pahole js::jit::ABIArg
offset size
0 16 : struct js::jit::ABIArg {
0 4 : kind_ : js::jit::ABIArg::Kind
4 4 : --> 32 bit hole in js::jit::ABIArg <--
8 8 : u : struct union {...} {
8 +0 1 : gpr_ : js::jit::Register::Code
8 +0 8 : fpu_ : js::jit::FloatRegister::Code
8 +0 4 : offset_ : uint32_t
} union {...}
} js::jit::ABIArg
I am working on a program that is creating IRIG106 Chapter 10 data for a cube-sat project. Currently it is being implemented in python and I am having difficulty implementing the final component of the Chapter 10 header.
The way I have implemented it I am currently finding checksum values that are larger than what will fit inside of an integer of the size defined by the specification (2 bytes).
The standard defines the header checksum in section 10.6.1.1 paragraph "J" of the IRIG 106-09 standard. It is defined as the following:
J Header Checksum. (2 Bytes) contains a value representing a 16-bit arithmetic sum of all 16-bit words in the header excluding the Header Checksum Word.
There is also a programming manual provided that has example C code that shows the following (from page A-2-17):
uint16_t I106_CALL_DECL uCalcHeaderChecksum(SuI106Ch10Header * psuHeader)
{
int iHdrIdx;
uint16_t uHdrSum;
uint16_t * aHdr = (uint16_t *)psuHeader;
uHdrSum = 0;
for (iHdrIdx=0; iHdrIdx<(HEADER_SIZE-2)/2; iHdrIdx++)
uHdrSum += aHdr[iHdrIdx];
return uHdrSum;
}
I have implemented the following in Python using the BitString library:
def calculate_checksum(byte_data: BitArray = None, header_length_bytes: int = 24, chunk_length: int = 16):
# Set the checksum to zero:
checksum = 0
# Loop through the Chapter 10 header and compute the 16 bit arithmetic sum:
for bit_location in range(0, (header_length_bytes-2), chunk_length):
# Get the range of bits to select:
bit_range = slice(bit_location, (bit_location + chunk_length))
# Get the uint representation of the bit data found:
checksum += Bits(bin=byte_data.bin[bit_range]).uint
# Write the computed checksum as binary data to the start location of the checksum in the header:
byte_data.overwrite(Bits(uint=checksum, length=chunk_length), (header_length_bytes-2*8))
Any thoughts or insights you could provide would be extremely appreciated. I know it should be a simple solution but I am just not able to see it.
--- Update 2 ---
I tried doing both roll over and truncation and they both produced the same result:
test_value = 2**16
test_value1 = test_value + 500
test_value2 = test_value1 % (2**16) -> 500
test_value3 = test_value1 & 0xFFFF -> 500
--- Update 3 ---
When I compare the execution of the python and C checksum functions I have run into the following using these values as an input per the spec:
Sync = "EB25" (2 bytes)
ChannelID = 1 (2 bytes)
PacketLen = 1024 (4 bytes)
When I compare the outputs at each step I see the following:
C:
Header0: EB25
index = 0 16bit chunk = 60197 checksum = 60197
Header1: 0001
index = 1 16bit chunk = 1 checksum = 60198
Header2: 0400
index = 2 16bit chunk = 1024 checksum = 61222
Header3: 0000
index = 3 16bit chunk = 0 checksum = 61222
Python:
eb25
index: 0 chunk: 60197 checksum: 60197
0001
index: 1 chunk: 1 checksum: 60198
0000
index: 2 chunk: 0 checksum: 60198
0400
index: 3 chunk: 1024 checksum: 61222
So, I know this question is really old, but I had the same issue. The endianess of the packet matters. In a .ch10 file, the packet start is 0x25EB because each section is little endian. Here's how I'm doing everything right now in C-ish code.
// read until the end of the file
while (!atEndOfFile)
{
// verify that we get teh sync packet
if ( readNextByte == 0x25 )
{
if ( readNextByte == 0xEB )
{
// store the sync packet
byte packetHeader[24];
packetHeader[0] = 0x25;
packetHeader[1] = 0xEB
// grab the rest of the header
for ( int i = 2; i < 24; j++ )
{
packetHeader[j] = readNextByte;
}
// grab the check sum from the packet
uint16 actualCheckSum = (packetHeader[23] << 8) |
packetHeader[22];
// calculate the checkSum
uint16 calculatedCheckSum = 0;
for ( int i = 0; i < 22; i++ )
{
calculatedCheckSum += (packetHeader[i + 1] << 8) |
packetHeader[i];
}
// verify the calculation
if ( calculatedCheckSum == actualChecksum )
{
printLine( "We calculated the checksum!");
printLine( "actual checksum: " + actualCheckSum +
"calculated checksum" + calculatedCheckSum );
}
}
}
}
I haven't done enough digging into the irig106 library, but I believe that it handles the translation when it reads in a .ch10 file.
I want to use a dll function which returns AP ssid list in Python, But it takes a preallocated struct with dyamic length array inside. I don't know how to define such a structure, without knowing the returned array length in advance.
Below is how the definition looks like in the C# demo; specifically the SSID byte array length in this struct varies.
public extern static bool D300SysUI_WiFiGetAroundSsidStatus(IntPtr SSIDList, int nMaxCount);
public struct SSIDLISTNET
{
public uint ATIMWindow;
public D300SysUI.NDIS_802_11_AUTHENTICATION_MODE AuthenticationMode;
public uint BeaconPeriod;
public uint DSConfig;
public uint DwellTime;
public uint HopPattern;
public uint HopSet;
public D300SysUI.NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
public byte[] MacAddress;
public D300SysUI.NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
public uint NumberOfItems;
public uint Privacy;
public byte[] Reserved;
public int Rssi;
public byte[] Ssid;
public uint SsidLength;
public byte[] SupportedRates;
}
Do I need to create_string_buffer long enough by estimation ? And loop through the returned buffer, byte by byte and assmebly the bytes into element by size?
If that is the right way, how do I determine the end of the dymamic arrays ? (please pardon my ignorance, I am new to ctypes/c++)
PS: Example from the C# SDK
//D300SysUI.SSIDLIST[] items= new D300SysUI.SSIDLIST[30];
//IntPtr[] ptArray = new IntPtr[1];
//ptArray[0] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(D300SysUI.SSIDLIST)) * 30);
//bool b = D300SysUI.D300SysUI_WiFiGetAroundSsidStatus(ptArray[0], 30);
//string message = "";
//string mac = "";
//if (b)
//{
// items[0] = (D300SysUI.SSIDLIST)Marshal.PtrToStructure((IntPtr)((UInt32)ptArray[0]), typeof(D300SysUI.SSIDLIST));
// for (int i =0;i<6;i++)
// {
// mac += String.Format("{0:X2} ", items[0].MacAddress[i]);
// }
// message += string.Format("AP:{0},MAC:{1},dBm:{2} \r\n",Encoding.GetEncoding("ASCII").GetString(items[0].Ssid,0,(int)(items[0].SsidLength)),mac,items[0].Rssi);
// for (int j = 1; j < items[0].NumberOfItems; j++)
// {
// items[j] = (D300SysUI.SSIDLIST)Marshal.PtrToStructure((IntPtr)((UInt32)ptArray[0] + j * Marshal.SizeOf(typeof(D300SysUI.SSIDLIST))), typeof(D300SysUI.SSIDLIST));
// mac = "";
// for (int i = 0; i < 6; i++)
// {
// mac += String.Format("{0:X2} ", items[j].MacAddress[i]);
// }
// message += string.Format("AP:{0},MAC:{1},dBm:{2} \r\n", Encoding.GetEncoding("ASCII").GetString(items[j].Ssid, 0, (int)(items[j].SsidLength)), mac, items[j].Rssi);
// }
//}
//Marshal.FreeHGlobal(ptArray[0]);
//MessageBox.Show(message);
If you have defined SSIDLISTNET as a cTypes structure, you just allocate the desired number of these. If the maximum the API will return is 30, allocating that number of instances is a simple and straightforward solution.
SSIDlist = SSIDLISTNET * 30
If you are very constrained on memory, you can probably copy over the required actual number of items to a new list which only holds that many, and del SSIDlist to free up the memory you reserved for this list (or let it go out of scope).
We are currently working on an Arduino Uno project and getting stuck at the conversion of the integer data to degrees celcius. This code is working however it converts the binary packed data (\xd01) etc. to int (0-255). Our question is: how to convert the integer value to read out a certain degree of Celcius. For example: int 2 = 2 degrees celcius and 255 = 35 degrees Celcius
This is our Python code with the Pyserial module
import serial
import struct
ser = serial.Serial('COM3', 19200, timeout=5)
while True:
tempdata = ser.read(2)
x= struct.unpack('!BB', tempdata)
print(x)
And this is the code of the temperature conversion on our Arduino Uno, it is written in C.
#define F_CPU 16E6
// output on USB = PD1 = board pin 1
// datasheet p.190; F_OSC = 16 MHz & baud rate = 19.200
#define UBBRVAL 51
void uart_init()
{
// set the baud rate
UBRR0H = 0;
UBRR0L = UBBRVAL;
// disable U2X mode
UCSR0A = 0;
// enable transmitter
UCSR0B = _BV(TXEN0);
// set frame format : asynchronous, 8 data bits, 1 stop bit, no parity
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);
}
void transmit(uint8_t data)
{
// wait for an empty transmit buffer
// UDRE is set when the transmit buffer is empty
loop_until_bit_is_set(UCSR0A, UDRE0);
// send the data
UDR0 = data;
}
void init_adc()
{
// ref=Vcc, left adjust the result (8 bit resolution),
// select channel 0 (PC0 = input)
ADMUX = (1<<REFS0);
// enable the ADC & prescale = 128
ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}
uint8_t get_adc_value()
{
//ADMUX |= 1
ADCSRA |= (1<<ADSC); // start conversion
loop_until_bit_is_clear(ADCSRA, ADSC);
return ADC; // 8-bit resolution, left adjusted
}
/*
((value / 1024 * 5) - 0. 5) * 100
*/
int main(void) {
init_adc();
uart_init();
//int x;
while(1)
{
int x = get_adc_value();
int temp = ((((float) x / 1024) * 5) - 0.5) * 100;
transmit(temp);
_delay_ms(200);
}
}
The conversion from ADC value to temp will most likely depend on what type of temperature sensor you are using. I recommend looking a the datasheet of your temp sensor.
If you are using a 'TMP36', you can convert using this formula:
Centigrade temperature = [(analog voltage in mV) - 500] / 10
Source: https://learn.adafruit.com/tmp36-temperature-sensor/using-a-temp-sensor
If you a using a thermocouple, you'll need to look at the table of correspondance for the type you are using (e.g. type K: https://www.omega.fr/temperature/Z/pdf/z204-206.pdf)
As my first foray into programming with python (previous exp only with vba and very basic java), I am trying to replicate some functionality of a GUI application, osPID Frontend, written in Processing, which communicates with an Arduino sketch, osPID-Firmware, over USB serial.
The front end takes a float array, converts it to a byte array, and then sends this over the serial link.
I have been trying to test sending a byte array to the device using the following python code:
import serial
def Send_Dash(myPort):
#To_Controller()
byteSet = 1,2,3 #bytes(1),bytes(4),bytes(3)
toSend = bytearray(byteSet)
myPort.write('5')
myPort.write('1')
myPort.write(toSend)
myPort = serial.Serial('/dev/ttyUSB0',9600,8,'N',timeout=None) # open first serial port
if myPort.isOpen():
myPort.read(10)
#typeReq = bytearray('0,0')
#myPort.write(typeReq)
Send_Dash(myPort)
Unfortunately which I can connect ok, I don't seem to be able to send any successful commands. I'd like to come up with some generic function I can use to send commands to the arduino (either by replicating the Processing code in python, or doing away with the byte array structure entirely (if possible). The original code was written to allow values such as 300000 and 0.000001 to the device. I will be unlikely to send anything greater than 300, or with decimals below 0.001, but as I hope to release this open source, others might.
For reference, the relevant arduino and Processing functions are below:
The function which converts the float array to a byte array is:
byte[] floatArrayToByteArray(float[] input)
{
int len = 4*input.length;
int index=0;
byte[] b = new byte[4];
byte[] out = new byte[len];
ByteBuffer buf = ByteBuffer.wrap(b);
for(int i=0;i<input.length;i++)
{
buf.position(0);
buf.putFloat(input[i]);
for(int j=0;j<4;j++) out[j+i*4]=b[3-j];
}
return out;
}
An example of one of the functions which packs the data is:
// Sending Floating point values to the arduino
// is a huge pain. if anyone knows an easier
// way please let know. the way I'm doing it:
// - Take the 6 floats we need to send and
// put them in a 6 member float array.
// - using the java ByteBuffer class, convert
// that array to a 24 member byte array
// - send those bytes to the arduino
void Send_Dash()//To_Controller()
{
float[] toSend = new float[3];
toSend[0] = float(SPField.getText());
toSend[1] = float(InField.getText());
toSend[2] = float(OutField.getText());
Byte a = (AMLabel.valueLabel().getText()=="Manual")?(byte)0:(byte)1;
byte identifier = 1;
myPort.write(identifier);
myPort.write(a);
myPort.write(floatArrayToByteArray(toSend));
}
A more simple function is:
void Run_Profile()
{
byte[] toSend = new byte[2];
toSend[0]=8;
toSend[1]=1;
myPort.write(toSend);
}
The byte array is made into a union:
boolean ackDash = false, ackTune = false;
union { // This Data structure lets
byte asBytes[32]; // us take the byte array
float asFloat[8]; // sent from processing and
} // easily convert it to a
foo; // float array
And read by SerialReceive(), which takes action based on the data:
void SerialReceive()
{
// read the bytes sent from Processing
byte index=0;
byte identifier=0;
byte b1=255,b2=255;
boolean boolhelp=false;
while(Serial.available())
{
byte val = Serial.read();
if(index==0){
identifier = val;
Serial.println(int(val));
}
else
{
switch(identifier)
{
case 0: //information request
if(index==1) b1=val; //which info type
else if(index==2)boolhelp = (val==1); //on or off
break;
case 1: //dasboard
case 2: //tunings
case 3: //autotune
if(index==1) b1 = val;
else if(index<14)foo.asBytes[index-2] = val;
break;
case 4: //EEPROM reset
if(index==1) b1 = val;
break;
case 5: //input configuration
if (index==1)InputSerialReceiveStart();
InputSerialReceiveDuring(val, index);
break;
case 6: //output configuration
if (index==1)OutputSerialReceiveStart();
OutputSerialReceiveDuring(val, index);
break;
case 7: //receiving profile
if(index==1) b1=val;
else if(b1>=nProfSteps) profname[index-2] = char(val);
else if(index==2) proftypes[b1] = val;
else foo.asBytes[index-3] = val;
break;
case 8: //profile command
if(index==1) b2=val;
break;
default:
break;
}
}
index++;
}
//we've received the information, time to act
switch(identifier)
{
case 0: //information request
switch(b1)
{
case 0:
sendInfo = true;
sendInputConfig=true;
sendOutputConfig=true;
break;
case 1:
sendDash = boolhelp;
break;
case 2:
sendTune = boolhelp;
break;
case 3:
sendInputConfig = boolhelp;
break;
default:
break;
}
break;
case 1: //dashboard
if(index==14 && b1<2)
{
setpoint=double(foo.asFloat[0]);
//Input=double(foo.asFloat[1]); //Not used
if(b1==0)
{
output=double(foo.asFloat[2]);
}
}
break;
case 2: //Tune
if(index==14 && (b1<=1))
{
kp = double(foo.asFloat[0]); //
ki = double(foo.asFloat[1]); //
kd = double(foo.asFloat[2]); //
}
break;
case 3: //ATune
if(index==14 && (b1<=1))
{
aTuneStep = foo.asFloat[0];
aTuneNoise = foo.asFloat[1];
aTuneLookBack = (unsigned int)foo.asFloat[2];
}
break;
default:
break;
}
}
Edit:
Sorry Lesto, yes I did read the duplicate answers, but I've only just figured out how to use struct.pack (I think!).
It appears that the existing Processing code packs (up to) 6 floats into a 32 byte array, using ByteBuffer. 6 floats is 24 bytes using struct.pack - although I gather I can pad this out by adding 1s and 0s into the format string, provided I know where they should go?
Following the comments by lesto, I have had another try (but still not quite there).
import struct
import serial
from array import array
def packByteArray(formatToSend):
bytesToSend = bytearray()
#bytesToSend.extend(struct.pack('#fffff',1,0,20,0,10) #Different result?
bytesToSend.extend(struct.pack(formatToSend,1)) #request Type
bytesToSend.extend(struct.pack(formatToSend,0)) #identifier
bytesToSend.extend(struct.pack(formatToSend,20)) #Setpoint
bytesToSend.extend(struct.pack(formatToSend,0)) #Input
bytesToSend.extend(struct.pack(formatToSend,10)) #Output
return bytesToSend
def main():
myPort = serial.Serial('/dev/ttyUSB0',9600,8,'N',timeout=None)
#Check if port is open, and if so, send the byta array.
if myPort.isOpen():
thisVar = packByteArray('>f')
print len(thisVar)
myPort.write(thisVar)
if __name__ == '__main__':
main()