Having timer collision issue with using Servo and SoftwareSerial - timer

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.

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

How to use Modbus with Microchip PIC24

First time using Modbus. I'm trying to send a request to read a temperature value from my slave device (ID is 0x01). I'm sending the command through UART and viewing command on my logic analyser.
Here is my code:
void temp_sensor()
{
//Transmit RTU to Inisitu probe to fetch Temperature and units
Flow_Control_SetHigh();
unsigned char TempRTU[]= {0x01, 0x03, 0x00, 0x2D, 0x00, 0x02, 0x54, 0x02};
int i;
int data_len = 8;
for (i = 0; i < data_len; i++) {
UART1_Write(TempRTU[i]);
}
}
0x01: Slave ID:
0x03: function code:
0x002D: Read temperature register:
0x0002: Read 2 registers:
0x5402: CRC Checksum:
The hex string is correct when viewing through UART on my analyser, however when setting the analyser to view Modbus, the string is not even close to being correct. I've attached a screenshot of my analyser while viewing the Modbus signal.
Has anyone got experience with Modbus and PIC microcontrollers?

GT-511C3 Fingerprint Scanner with STM32F407G Microcontroller

I am working on a project that requires using the GT-511C3 fingerprint scanner with a STM32F407G microcontroller board. I am using Keil uvision 5 and can't figure out how to initialize and turn on the scanner. I've written this code and it seems correct, based off the datasheet, for starting up the scanner and sending the start commands. I've used an oscilloscope to verify that the values are being sent correctly and it seems they are but the scanner is not lighting up. I'm completely lost on what I could be doing wrong or what I could be missing. I've verified the scanner is wired correctly and connected to the board accordingly. Any help would be gr8 :)
Scanner: https://www.digikey.com/catalog/en/partgroup/fingerprint-scanner-ttl-gt-511c3/56722
Current Code:
#include "stm32f4xx.h" // Device header
int i=0;
int z=0;
int main(){
// ENABLE CLocks for PORT C and UART4
RCC -> AHB1ENR = 0x00000004; //Enable the clock to GPIO port GPIOCEN
RCC -> APB1ENR = 0x00080000; //Enable the clock to UART 4 UART4EN
// Set Mode Pins on Port C pins 10 and 11 to Alternate Function
GPIOC->MODER|=0x00A00000;
// Set Alternate Function Register for Port C 10 and 11
GPIOC->AFR[1]|= 0x00008800; //AFR High = enabled
// UART setup:
// 8 data bits (default), no parity (default), one stop bit (default) & no flow control (default)
// UART Baud Rate
UART4->BRR = 0x683;
// Enable TRANSMITTER (TE bit)
UART4->CR1 |= (1<<3);
UART4->CR1 |= (1<<2);
// WE WANT TO TRANSMIT SO ENABLE TRANSMIT INTERRUPT
UART4->CR1 |=(1<<7);
// Enable UART (UE bit in CR1 register)
UART4->CR1 |=(1<<13);
// NVIC to handle interrupts
__enable_irq();
NVIC_ClearPendingIRQ(UART4_IRQn);
NVIC_SetPriority(UART4_IRQn,0);
NVIC_EnableIRQ(UART4_IRQn);
while(1){};
}
// Interrupt reoutine for UART1
void UART4_IRQHandler(void){
// TX IRQ
if(((UART4->SR)&(1<<7))>0){ //if txe flag in sr is on then
if(z==0){
int command[]={0x55,0xAA, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x01};
UART4->DR=command[i]; // command start code
i=i+1;
if(i==12){
z=1;
i=0;
}
}
if(z==1){
int command1[]={0x55,0xAA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x00, 0x13, 0x01};
UART4->DR=command1[i]; // command start code
i=i+1;
if(i==12){
z=0;
i=0;
}
}
}
}

Polling uBlox MAX-6Q

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.

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