Offline
Newbie
Karma: 0
Posts: 40
|
 |
« on: May 03, 2012, 08:06:22 am » |
Hello, For use on our tractor puller we where looking for an uC and since there are a lot of them I looked at open-source, large user group and support so Arduino is the most obvious choice. The device have to react fast, within 20 microseconds and arduino's probably will do but I must not screwup with coding. So the main question is; is the arduino platform capable? Our setup isn't that complicated, since it is a tractor puller no fancy stuff is needed, it only have to trigger the injection and control injection time depending on throttle position. (potentio meter) The potentio meter also controls the pressure of the pump directly trough hardware and the timing of the injection is done by a rotating disc and infrared LED's so not much calculating things for the device. I did try to make some code using the examples and came up with the following: /* Cuprum-2.0 simplified common rail injection system. Use interrupts for timing injections cycle, a timing disc (alternator) is mounted to camshaft. Attach the center pin of a potentiometer to pin A0, and the outside pins to +5V and ground, output will be 0-1023. Potentiometer used is stereo, second line will be used to control injection pump pressure. Attach timing signals: cyl1 pin 10, cyl2 pin 11, cyl3 pin 12 and cyl4 pin 13. Attach injector hardware: cyl1 pin 4, cyl2 pin 5, cyl3 pin 6 and cyl4 pin 7. */
// setup pin for throttle potentiometer: int ThrottlePin = A0; int ThrottleValue = 0; // define throttle calibration values: // 9000rpm = 150rps, 1000000us/150 = 6666us per rotation. // 6666/360*30 = 555us injection time, 30 crank degrees at 9000rpm. // 1024/555 = 1.8 int rpm = 9000.0; int angle = 30.0; int ThrottleCalib = 1024.0/(1000000.0/(rpm/60.0)/360.0*angle); // setup timer pins for interrupts: int cyl1 = 10; int cyl2 = 11; int cyl3 = 12; int cyl4 = 13; volatile int state = LOW; // setup pins for injector drivers: int inj1 = 4; int inj2 = 5; int inj3 = 6; int inj4 = 7;
void setup() { // attach action when interrupt gets actived: pinMode(cyl1, OUTPUT); attachInterrupt(0, inject1, RISING); pinMode(cyl2, OUTPUT); attachInterrupt(0, inject2, RISING); pinMode(cyl3, OUTPUT); attachInterrupt(0, inject3, RISING); pinMode(cyl4, OUTPUT); attachInterrupt(0, inject4, RISING); // initialize the digital injector pins as an output: pinMode(inj1, OUTPUT); pinMode(inj2, OUTPUT); pinMode(inj3, OUTPUT); pinMode(inj4, OUTPUT); }
void loop() { // read the input on analog pin 0: ThrottleValue = analogRead(ThrottlePin); // recalculate throttle value to wanted microseconds: ThrottleValue = ThrottleValue / ThrottleCalib; // check the interrupts: digitalWrite(cyl1, state); digitalWrite(cyl2, state); digitalWrite(cyl3, state); digitalWrite(cyl4, state); }
void inject1() { digitalWrite(inj1, HIGH); delayMicroseconds(ThrottleValue); digitalWrite(inj1, LOW); }
void inject2() { digitalWrite(inj2, HIGH); delayMicroseconds(ThrottleValue); digitalWrite(inj2, LOW); }
void inject3() { digitalWrite(inj3, HIGH); delayMicroseconds(ThrottleValue); digitalWrite(inj3, LOW); }
void inject4() { digitalWrite(inj4, HIGH); delayMicroseconds(ThrottleValue); digitalWrite(inj4, LOW); }
|
|
|
|
|
Logged
|
|
|
|
|
Seattle, WA USA
Offline
Brattain Member
Karma: 311
Posts: 35470
Seattle, WA USA
|
 |
« Reply #1 on: May 03, 2012, 08:15:00 am » |
The device have to react fast, within 20 microseconds and arduino's probably will do but I must not screwup with coding.
So the main question is; is the arduino platform capable? React to what? Being hit with a sledgehammer? It will react to that, nearly instantaneously. int rpm = 9000.0; int angle = 30.0; Clearly, you need to learn the difference between ints and floats. attachInterrupt(0, inject1, RISING); pinMode(cyl2, OUTPUT); attachInterrupt(0, inject2, RISING); pinMode(cyl3, OUTPUT); attachInterrupt(0, inject3, RISING); pinMode(cyl4, OUTPUT); attachInterrupt(0, inject4, RISING); After all this, interrupt 0 has inject4 assigned as the callback function. You might as well delete the others. You can NOT call any form of delay in an interrupt service routine.
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
UK
Offline
Brattain Member
Karma: 136
Posts: 18992
I don't think you connected the grounds, Dave.
|
 |
« Reply #2 on: May 03, 2012, 08:52:46 am » |
You can NOT call any form of delay in an interrupt service routine. sp. "You should NOT call any form of delay in an interrupt service routine." Yes, you can use Arduino to do what you want. You may have to go slightly off-piste, but I don't really see that interrupts are necessary or desirable.
|
|
|
|
« Last Edit: May 03, 2012, 08:54:52 am by AWOL »
|
Logged
|
Pete, it's a fool looks for logic in the chambers of the human heart.
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 40
|
 |
« Reply #3 on: May 03, 2012, 08:57:34 am » |
Thanks for your input Paul, int rpm = 9000.0; int angle = 30.0; int ThrottleCalib = 1024.0/(1000000.0/(rpm/60.0)/360.0*angle);
Reason was that I want the calculation to be accurate but the result being rounded up and have no clue how to that otherwise, will search on a float solution. pinMode(cyl1, OUTPUT); attachInterrupt(0, inject1, RISING); pinMode(cyl2, OUTPUT); attachInterrupt(1, inject2, RISING); pinMode(cyl3, OUTPUT); attachInterrupt(2, inject3, RISING); pinMode(cyl4, OUTPUT); attachInterrupt(3, inject4, RISING);
So this is what I meant? (if INPUT gets high OUTPUT is triggered for all 4 separately) Why can't I call a delay within the interrupt routine? Is this not possible or a coding no-no? Theoretically the interrupt won't be actived before the timer ends, if that happens in practice it means I got 30K rpm and I am pretty sure my rods are a bigger problem then the software if that happens... p.s. the Sledgehammer pulling team are friends so they won't hit me. ;-)
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 40
|
 |
« Reply #4 on: May 03, 2012, 09:00:08 am » |
"You should NOT call any form of delay in an interrupt service routine." OK, coding no-no I presume. Maybe a suggestion to do it otherwise?
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
UK
Offline
Brattain Member
Karma: 136
Posts: 18992
I don't think you connected the grounds, Dave.
|
 |
« Reply #5 on: May 03, 2012, 09:04:32 am » |
I'd probably do the whole thing in a simple loop. 1) see if the last analogue conversion has finished, and if it has, read the result and map it, and start another analogue conversion 2) for i = 0 to 'n'-1 cylinders Busy-wait on the sensor fire injector [n] delay un-fire injector [n]
|
|
|
|
|
Logged
|
Pete, it's a fool looks for logic in the chambers of the human heart.
|
|
|
|
Seattle, WA USA
Offline
Brattain Member
Karma: 311
Posts: 35470
Seattle, WA USA
|
 |
« Reply #6 on: May 03, 2012, 09:22:38 am » |
So this is what I meant? Depends on which Arduino you are using. The 328-based Arduinos only have two external interrupt pins (pins 2 and 3 that are interrupt #0 and interrupt #1). int rpm = 9000.0; int angle = 30.0; 9000 is an int. 9000.0 is a float. Which one do you think you can store in an int? Why can't I call a delay within the interrupt routine? Because interrupt routines are supposed to be as fast as possible, so that they don't interfere with other interrupt based things, like the clock. delay(), delayMicroseconds(), or a bunch of nop assembler calls are all wasting time, which you don't want to do in an interrupt service routine.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 40
|
 |
« Reply #7 on: May 03, 2012, 11:20:51 am » |
Ok guys, first thank you for having patience with me and helping out. (my only coding skills where with bash and at age 44 I am not that quick anymore  About adding a decimal to the int, I read somewhere that automatically triggers a float calculation or creates a workaround for rounding up values before calculation was finished so I changed them to float. Also removed the calibration calculation for sake of speed. Using a while or a for loop is not an option, onboard the puller life is pretty hard and I don't want to risk a complete engine failure because the software is waiting for a signal that is not gonna come due to a broken wire or other damage. In the past I had an electronic device doing the same thing, an infrared transmitter/receiver combo triggered an NE555 which controlled the duration so that is why I want to use interrupts, intimidate response nomather what. Aren't all digital ports usable as interrupts? Or do I need to add some extra function to accomplish that? /* Cuprum-2.0 simplified common rail injection system. Use interrupts for timing injections cycle, a timing disc (alternator) is mounted to camshaft. Attach the center pin of a potentiometer to pin A0, and the outside pins to +5V and ground, output will be 0-1023. Potentiometer used is stereo, second line will be used to control injection pump pressure. Attach timing signals: cyl1 pin 10, cyl2 pin 11, cyl3 pin 12 and cyl4 pin 13. Attach injector hardware: cyl1 pin 4, cyl2 pin 5, cyl3 pin 6 and cyl4 pin 7. */
// setup pin for throttle potentiometer: int ThrottlePin = A0; float ThrottleValue = 0; // define throttle calibration values: // 9000rpm = 150rps, 1000000us/150 = 6666us per rotation. // 6666/360*30 = 555us injection time, 30 crank degrees at 9000rpm. // 1024/555 = 1.8 float ThrottleCalib = 1.85; // setup timer pins for interrupts: int cyl1 = 10; int cyl2 = 11; int cyl3 = 12; int cyl4 = 13; volatile int state = LOW; // setup pins for injector drivers: int inj1 = 4; int inj2 = 5; int inj3 = 6; int inj4 = 7;
void setup() { // attach action when interrupt gets actived: pinMode(cyl1, OUTPUT); attachInterrupt(1, inject1, RISING); pinMode(cyl2, OUTPUT); attachInterrupt(2, inject2, RISING); pinMode(cyl3, OUTPUT); attachInterrupt(3, inject3, RISING); pinMode(cyl4, OUTPUT); attachInterrupt(4, inject4, RISING); // initialize the digital injector pins as an output: pinMode(inj1, OUTPUT); pinMode(inj2, OUTPUT); pinMode(inj3, OUTPUT); pinMode(inj4, OUTPUT); }
void loop() { // read the input on analog pin 0: ThrottleValue = analogRead(ThrottlePin); // recalculate throttle value to wanted microseconds: ThrottleValue = ThrottleValue / ThrottleCalib; // check the interrupts: digitalWrite(cyl1, state); digitalWrite(cyl2, state); digitalWrite(cyl3, state); digitalWrite(cyl4, state); }
void inject1() { digitalWrite(inj1, HIGH); delayMicroseconds(ThrottleValue); digitalWrite(inj1, LOW); }
void inject2() { digitalWrite(inj2, HIGH); delayMicroseconds(ThrottleValue); digitalWrite(inj2, LOW); }
void inject3() { digitalWrite(inj3, HIGH); delayMicroseconds(ThrottleValue); digitalWrite(inj3, LOW); }
void inject4() { digitalWrite(inj4, HIGH); delayMicroseconds(ThrottleValue); digitalWrite(inj4, LOW); }
|
|
|
|
|
Logged
|
|
|
|
|
Greenville, IL
Offline
Edison Member
Karma: 11
Posts: 1288
Warning Novice on board! 0 to 1 chance of errors!
|
 |
« Reply #8 on: May 03, 2012, 11:59:29 am » |
Here is a rough sketch, I wrote this based on 2 examples provided with the Arduino IDE "blink without delay", and "Analog Input". The sketch is written to read one trigger sensor, a throttle pot., and be able to fire one injector. You will have to modify the sketch to read 4 injector sensors, and fire 4 injectors. Perhaps this is enough to get you going. Keep in mind I am a Novice at programming and there is probably lots of room for improvement.  /* BASED ON EXAMPLES: Analog Input, Blink without Delay */ int cyl1 = 10; //cam trigger sensor for injector 1 int trigger1 = LOW; //variable to hold state of sensor for injector 1 int injectorpin = 13; // the pin for the injector solenoid int sensorPin = A0; // the input pin for the throtttle potentiometer int sensorValue = 0; // variable to store the value coming from the sensor long previousMillis = 0; // will store last time injection time was updated
// the follow variables is a long because the time, measured in miliseconds, // will quickly become a bigger number than can be stored in an int. long interval = 1000; // interval at which to fire injectors(milliseconds)
void setup() { // declare the injectorpin as an OUTPUT: pinMode(injectorpin, OUTPUT); pinMode (cyl1, INPUT); }
void loop() {
// read the value from the sensor: sensorValue = analogRead(sensorPin); interval = sensorValue;////////////////////////////////////add mapping here or formula unsigned long currentMillis = millis(); //update currentMillis trigger1 = digitalRead(cyl1); //read the trigger1 if(trigger1 == HIGH) { // turn the injectorpin on digitalWrite(injectorpin, HIGH); previousMillis = currentMillis; //note the time of firing the injector } if (trigger1 == LOW && (currentMillis - previousMillis) > interval) //if trigger is off and injector has been on long enough, turn off injector { // turn the injectorpin off: digitalWrite(injectorpin, LOW); } }
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 40
|
 |
« Reply #9 on: May 05, 2012, 01:31:43 am » |
Hello boys, thanks for thinking with me. If using a loop it opens another possibility to me, a rotary encoder for timing: http://nl.rs-online.com/web/p/rotary-encoders/2603780/?searchTerm=260-3780&relevancy-data=636F3D3126696E3D4931384E525353746F636B4E756D6265724D504E266C753D656E266D6D3D6D61746368616C6C26706D3D5E5C647B337D5B5C732D2F255C2E5D5C647B332C347D2426706F3D313426736E3D592673743D52535F53544F434B5F4E554D424552267573743D3236302D333738302677633D4E4F4E4526 (no clue how to hock it up) int posPin = A1; // the input pin for position sensor int posValue = 0; // variable to store the value from the sensor int injectorpin1 = 13; // the pin for the injector solenoid1 int injectorpin2 = 14; // the pin for the injector solenoid2 int injectorpin3 = 15; // the pin for the injector solenoid3 int injectorpin4 = 16; // the pin for the injector solenoid4 int sensorPin = A0; // the input pin for the throtttle potentiometer int sensorValue = 0; // variable to store the value coming from the sensor long previousMillis = 0; // will store last time injection time was updated
// the follow variables is a long because the time, measured in miliseconds, // will quickly become a bigger number than can be stored in an int. long interval = 1000; // interval at which to fire injectors(milliseconds)
void setup() { // declare the injector pins as an OUTPUT: pinMode(injectorpin1, OUTPUT); pinMode(injectorpin2, OUTPUT); pinMode(injectorpin3, OUTPUT); pinMode(injectorpin4, OUTPUT); }
void loop() {
// read the value from the sensor: sensorValue = analogRead(sensorPin); interval = sensorValue;////////////////////////////////////add mapping here or formula unsigned long currentMillis = millis(); //update currentMillis posValue = analogRead(posPin); if(posPin == 0) // of camshaft position is 0 degrees then fire cylinder 1 { // turn the injectorpin on digitalWrite(injectorpin1, HIGH); previousMillis = currentMillis; //note the time of firing the injector } if (posPin != 0 && (currentMillis - previousMillis) > interval) //if pos is not 0 degrees and injector has been on long enough, turn off injector { // turn the injectorpin off: digitalWrite(injectorpin1, LOW); }
if(posPin == 256) // of camshaft position is 90 degrees then fire cylinder 3 { // turn the injectorpin on digitalWrite(injectorpin3, HIGH); previousMillis = currentMillis; //note the time of firing the injector } if (posPin != 256 && (currentMillis - previousMillis) > interval) //if pos is not 90 degrees and injector has been on long enough, turn off injector { // turn the injectorpin off: digitalWrite(injectorpin3, LOW); } if(posPin == 512) // of camshaft position is 180 degrees then fire cylinder 4 { // turn the injectorpin on digitalWrite(injectorpin4, HIGH); previousMillis = currentMillis; //note the time of firing the injector } if (posPin != 512 && (currentMillis - previousMillis) > interval) //if pos is not 180 degrees and injector has been on long enough, turn off injector { // turn the injectorpin off: digitalWrite(injectorpin4, LOW); } if(posPin == 768) // of camshaft position is 270 degrees then fire cylinder 2 { // turn the injectorpin on digitalWrite(injectorpin2, HIGH); previousMillis = currentMillis; //note the time of firing the injector } if (posPin != 768 && (currentMillis - previousMillis) > interval) //if pos is not 270 degrees and injector has been on long enough, turn off injector { // turn the injectorpin off: digitalWrite(injectorpin2, LOW); } }
Is this the proper way to handle things? And will this be possible on a nano? p.s. the (currentMillis - previousMillis) > interval I don't understand. In bash to would mean "if $currentTime minus $previousTime is larger then Interval (throttle position) stop injecting"???
|
|
|
|
|
Logged
|
|
|
|
|
Seattle, WA USA
Offline
Brattain Member
Karma: 311
Posts: 35470
Seattle, WA USA
|
 |
« Reply #10 on: May 05, 2012, 08:02:36 am » |
int posPin = A1; // the input pin for position sensor int posValue = 0; // variable to store the value from the sensor int injectorpin1 = 13; // the pin for the injector solenoid1 Consistency is good. If you were consistent, the 3rd variable would be named injectorPin1, which would be much easier to read, in my opinion. long previousMillis = 0; // will store last time injection time was updated Time variables are unsigned long. You don't normally need to accommodate time running backwards. At least not in our universe. if(posPin == 0) // of camshaft position is 0 degrees then fire cylinder 1 if(posPin == 256) // of camshaft position is 90 degrees then fire cylinder 3 if(posPin == 512) // of camshaft position is 180 degrees then fire cylinder 4 if(posPin == 768) // of camshaft position is 270 degrees then fire cylinder 2 I don't know what kind of analog device you are reading, but depending on exact values from an ADC with a resolution of +/- 1 is not a good idea. Your injectors are going to not fire when they are supposed to, a lot. When to fire the injector. with respect to cam position, is typically a function of load, engine RPM, and a host of other sensor readings. I fear that you are oversimplifying a very complex process. I don't understand. In bash to would mean "if $currentTime minus $previousTime is larger then Interval (throttle position) stop injecting"??? It means, without the $, exactly the same thing in C.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 40
|
 |
« Reply #11 on: May 05, 2012, 11:31:47 am » |
long previousMillis = 0; // will store last time injection time was updated Time variables are unsigned long. You don't normally need to accommodate time running backwards. At least not in our universe. I don't understand. In bash to would mean "if $currentTime minus $previousTime is larger then Interval (throttle position) stop injecting"??? It means, without the $, exactly the same thing in C. I new that but I wanted to know what cyclegadgets reason was with that... if(posPin == 0) // of camshaft position is 0 degrees then fire cylinder 1 if(posPin == 256) // of camshaft position is 90 degrees then fire cylinder 3 if(posPin == 512) // of camshaft position is 180 degrees then fire cylinder 4 if(posPin == 768) // of camshaft position is 270 degrees then fire cylinder 2 I don't know what kind of analog device you are reading, but depending on exact values from an ADC with a resolution of +/- 1 is not a good idea. Your injectors are going to not fire when they are supposed to, a lot. When to fire the injector. with respect to cam position, is typically a function of load, engine RPM, and a host of other sensor readings. I fear that you are oversimplifying a very complex process. I have know clue how to read a rotary encoder so I presumed that it would give me an output from 0-1023 like a potentiometer would. (the documentation of the automotive encoder also was talking about 1024 steps) You are writing that the injectors will gonna skip firing with that resolution, is that due to the duration of a single encoder step compared to the time the loop lasts? If so a loop won't work with my timing disc either since at 9K rpm the pulse from that is about 1microsecond. (that is why I tried to use interrupts in my first code) A combustion engine isn't a very complex process at all, current automotive products make it complex due to drivability, environmental issues and so on. In tractor pulling we need an engine that runs at idle and at full throttle, we don't shift but open throttle and hold on. We have a 2.0l diesel engine making 400HP at 8000rpm with a boost pressure of 5bar, when hoocked on we rev up to 4000rpm (3bar of boost) and release the clutch. This is our conventional injected machine: https://picasaweb.google.com/lh/photo/4USFns0LHkMvU5lqaYSGmdMTjNZETYmyPJy0liipFm0?feat=directlinkAnd this is our common-rail, no uC used, ne555, HEF4044 and HEF4081. https://picasaweb.google.com/lh/photo/do7wdb7tZnFQsgpcPFTMmNMTjNZETYmyPJy0liipFm0?feat=directlinkThrottle controls the injection time, the fuel pressure (800-2500bar) and timing advance (-10 at idle and -35 at full throttle) so the only thing the uC has to do is read an input and opens an output for a throttle defined amount of time. (ok, 4 times in a cycle) If I understand correctly; rotation encoder isn't a good idea. using a loop either. So back to my first thought with using 4 interrupts and a distributor for giving injection pulses?
|
|
|
|
|
Logged
|
|
|
|
|
Seattle, WA USA
Offline
Brattain Member
Karma: 311
Posts: 35470
Seattle, WA USA
|
 |
« Reply #12 on: May 05, 2012, 02:39:12 pm » |
I have know clue how to read a rotary encoder so I presumed that it would give me an output from 0-1023 like a potentiometer would. No, it will not. A rotary encoder is a digital device. It is like a whole bunch of little switches. You need to read the encoder fairly often to detect the direction it is moving, and how many times the little switches have been pressed. There is an article on the playground explaining rotary encoders. Throttle controls the injection time, the fuel pressure (800-2500bar) and timing advance (-10 at idle and -35 at full throttle) so the only thing the uC has to do is read an input and opens an output for a throttle defined amount of time. (ok, 4 times in a cycle) So, when does the uC switch from -10 to -35 degrees? Doing that abruptly sounds like a bad idea to me. If I understand correctly; rotation encoder isn't a good idea. Using a rotary encoder is a fine idea, if you understand how to read it properly. So back to my first thought with using 4 interrupts and a distributor for giving injection pulses? You could do that with a Mega, with more external interrupts. The UNO only has two, and you'll probably want to use those to read the encoder, since that is the critical, time-dependent, input.
|
|
|
|
|
Logged
|
|
|
|
|
Greenville, IL
Offline
Edison Member
Karma: 11
Posts: 1288
Warning Novice on board! 0 to 1 chance of errors!
|
 |
« Reply #13 on: May 05, 2012, 04:04:02 pm » |
Insert Quote Quote from: PaulS on Today at 07:02:36 AM Code:
long previousMillis = 0; // will store last time injection time was updated
Time variables are unsigned long. You don't normally need to accommodate time running backwards. At least not in our universe. Quote I don't understand. In bash to would mean "if $currentTime minus $previousTime is larger then Interval (throttle position) stop injecting"??? It means, without the $, exactly the same thing in C.
I new that but I wanted to know what cyclegadgets reason was with that... That portion of code was based on an example from the Arduino IDE. I mostly just changed variable names. Turns out the example has the error of not using unsigned long for previousMillis and I did not catch it. Here is the example code link: http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 40
|
 |
« Reply #14 on: May 06, 2012, 04:39:11 am » |
So, when does the uC switch from -10 to -35 degrees? Doing that abruptly sounds like a bad idea to me.
The uC won't, the throttle cable is (was in the past) connected to distributor so more throttle means more advance. Using throttle for advance timing is discussionable but in practice it has proven to work very well, and gives a very responsive engine.  In the pic. the distributor is mounted on the front of the (old) engine directly on the camshaft pulley. (square aluminum box with lots of wires  Our previous device and schematics are over here: http://www.japie.deserver.nl/ftp/Cuprum/ if your interested. I have read the playground encoder stuff and I see what you mean, I was expecting it to give a different value for each step but they only give pulses so code is always needed for calculating the position. (and adding error possibility when a pulse is missed or doubled) I will use the exact setup as in the past with the alternator and a Mega to replace my electronics, thanks for helping me out...
|
|
|
|
|
Logged
|
|
|
|
|
|