|
|
- /*
- * ZMHW Project: Map
- *
- * A device to give you a visual representation of what Monitors
- * are active in your camera network.
- *
- * SteakElectronics, December 2018
- * Rare steak, well done electronics.
- *
- * Based on: telnet client
- *
- * Requires: UIPEthernet library
- *
- * Nice to haves in future:
- * PWM dimming, after alarms are off, so you know what was recently on.
- * Blinking on active alarms, instead of solid light, maybe.
- * Faster direct port access than digitalwrite. There are some libraries
- * but I don't know if they support the Mega. Instead, I'm going to rebuild the board
- * with a led matrix, and then use pwm pins, and I will deal with it then.
- * EDIT: instead maybre I'll use two 0402 resistors, and make a voltage divider, to allow for
- * dimmer led, with hardware instead of software.
- *
- * Troubleshooting Programming: When connecting, it can take 30-60 seconds
- * to speed this up, run the test suite telnet connect and it should connect
- * immediately after. The idea is that, it seems to connect as soon as a packet is
- * sent from zmtrigger. So send a test telnet packet, then it will connect after.
- */
-
-
-
- /*
- Telnet client
-
- This sketch connects to a a telnet server (http://www.google.com)
- using an Arduino Wiznet Ethernet shield. You'll need a telnet server
- to test this with.
- Processing's ChatServer example (part of the network library) works well,
- running on port 10002. It can be found as part of the examples
- in the Processing application, available at
- http://processing.org/
-
- Circuit:
- * Ethernet shield attached to pins 10, 11, 12, 13
-
- created 14 Sep 2010
- modified 9 Apr 2012
- by Tom Igoe
-
- */
-
- #include <SPI.h>
- #include <UIPEthernet.h>
-
-
- #define DEBUGMODE 0 // 1:on 0:off
- #define DEBUGMODE2 0
-
- // Enter a MAC address and IP address for your controller below.
- // The IP address will be dependent on your local network:
- byte mac[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x41
- };
- IPAddress ip(192, 168, 78, 60);
-
- // Enter the IP address of the server you're connecting to:
- IPAddress server(192, 168, 78, 123);
- // Port of ZMTrigger
- #define ZMTRIGGERPORT 6802
-
- // Initialize the Ethernet client library
- // with the IP address and port of the server
- // that you want to connect to (port 23 is default for telnet;
- // if you're using Processing's ChatServer, use port 10002):
- EthernetClient client;
-
- //Make a server
- //Not currently working.
- #define LISTENPORT 80 // (port 80 is default for HTTP)
- EthernetServer server2 = EthernetServer(LISTENPORT);
-
- //Globals
-
- char* zmtrigarray[50] = {0};
- uint8_t x = 0;
- uint8_t y = 0;
- uint8_t Mon1 = 0;
- uint8_t Mon2 = 0;
- uint8_t MonFIN = 0;
-
- char mask = 0x0F; //get last nibble
- uint8_t Mon1Nib = Mon1 & mask;
- uint8_t Mon2Nib = Mon2 & mask;
-
- char on = 0b01101110; //n (ascii)
- char off = 0b01100110; //f (ascii)
-
-
-
- int value = 0;
- int ADD = 3; //We start at digital pin 4. so add 3 to everything.
- //e.g. Monitor 3 is digital pin 6.
-
- uint8_t NEEDCOLOUR = 0;
-
-
-
-
- // Simple PWM
-
- //if this is 1, bypass interrupt / softpwm, else do softpwm
- uint8_t BypassSoftPWM[50] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
-
- int ledState[50] = {0}; // ledState used to set the LED
-
- #define LEDTIME 10000 // 2000 comes out to about 8 seconds
- uint16_t onInterval[50] = {LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME}; // How long to display dimmed led
-
- uint8_t FLIP[50] = {0};
- uint8_t PWMCounter = 0;
- uint8_t OffOverride[50] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
- uint8_t OffCtr[50] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
-
-
-
-
-
- //during interrupt, we SoftPWM any LEDs that are ready to be done so
- ISR(TIMER3_COMPA_vect) {
-
- for(x=1;x<51;x++){
- if(BypassSoftPWM[x] == 1){
-
- //Serial.print("Bypass activated for monitor: ");
- //Serial.println(x);
-
- ///FLIP[x] = 0;
- if(onInterval[x] == LEDTIME){
- //note: These serial prints, cause delays that mess up softpwm
- if(DEBUGMODE){
- Serial.print(F("Turning on SoftPWM for Monitor: "));
- Serial.println(x);
- }
- }
- //turn off
- //might be better to do equals for all cases
- if(PWMCounter < 7){
- digitalWrite(x + ADD, 0);
- }
-
- //turn on
- //must be after turn off, otherwise, if before, it will
- //immediately be turned off
- if(PWMCounter > 7){
- digitalWrite(x + ADD, 1);
- PWMCounter = 0;
- //Serial.print(F("Dim Led #: "));
- //Serial.println(x);
- }
-
-
-
- PWMCounter = PWMCounter + 1;
- //Serial.print("PWM Count is: ");
- //Serial.println(PWMCounter);
-
- //This is only for debug. Slows delay down.
- //Serial.print("oninterval is:");
- //Serial.println(onInterval[x]);
-
- onInterval[x] = onInterval[x] - 1;
- if (onInterval[x] == 0){
- BypassSoftPWM[x] = 0;
- //note: These serial prints, cause delays that mess up softpwm
- if(DEBUGMODE){
- Serial.print("Turning off softPWM on Monitor: ");
- Serial.println(x);
- }
- digitalWrite(x + ADD, 0);
- onInterval[x] = LEDTIME;
- //no need for softpwm override
- OffOverride[x] = 0;
- OffCtr[x] = 0;
- }
- }
-
- }
-
-
- }
-
-
- //Check if any monitors that didn't get the shutdown message
- ISR(TIMER4_COMPA_vect) {
-
- for(x=1;x<51;x++){
- if (OffOverride[x] == 1){
- OffCtr[x]++;
-
- if (OffCtr[x] == 50){
- OffOverride[x] = 0;
- OffCtr[x] = 0;
- digitalWrite(x + ADD, 0);
- Serial.print(F("Turning off due to override. Monitor #: "));
- Serial.println(x);
- sendPixel(0,10,10);
- }
-
- }
- }
-
- }
-
- void setup() {
-
-
- // in case we are restarting the setup loop because we didn't connect,
- // we don't want timer to interfere
- TCCR3A = 0;
- TCCR3B = 0;
- TCNT3 = 0;
-
- //RGB LED
- /*define Pin 2/PE4 as output*/
- /*Libraries included w/arduino IDE*/
- DDRE = 0b00010000;
-
- // Open serial communications and wait for port to open:
- Serial.begin(9600);
- while (!Serial) {
- ; // wait for serial port to connect. Needed for native USB port only
- }
- sendPixel(10,0,0);
- delay(500);
- sendPixel(0,10,0);
- delay(500);
- sendPixel(0,0,10);
-
-
- Serial.println("ZMHW Project: Map");
- //Using pins 4 to 49
- for (x=4;x<49;x++){
- pinMode(x, OUTPUT);
- digitalWrite(x, LOW);
- }
-
-
-
-
-
-
- // start the Ethernet connection:
- Ethernet.begin(mac, ip);
- // give the Ethernet shield a second to initialize:
- delay(1000);
- Serial.println("connecting...");
-
-
- // if you get a connection, report back via serial:
- if (client.connect(server, ZMTRIGGERPORT)) {
- Serial.println("connected");
-
- //Test all LEDs
- for (x=4;x<49;x++){
- digitalWrite(x, HIGH);
- delay(50);
- digitalWrite(x, LOW);
-
- }
- } else {
- // if you didn't get a connection to the server:
- Serial.println("connection failed");
-
-
- for(int err = 0; err<3;err++){
- sendPixel(0,10,0);
- delay(100);
- sendPixel(0,0,0);
- delay(100);
- }
- }
-
-
-
- // AVR Timer CTC Interrupts Calculator
- // v. 8
- // http://www.arduinoslovakia.eu/application/timer-calculator
- // Microcontroller: ATmega2560
- // Created: 2019-02-02T06:08:33.492Z
-
- //dimming is buggy. doesn't quite work.
- //problem is viewed with a scope, the timers seem to fault
- //only pwming on one led occasionally
- //setupTimer3();
- sendPixel(0,10,10);
- setupTimer4();
-
-
-
- }
-
- void loop() {
-
- // if there are incoming bytes available
- // from the server, read them and print them:
-
- //original
- /*if (client.available()) {
- char c = client.read();
- Serial.print(c);
- }*/
- //if (client.available()) {
- // char* zmtrigarray = client.read();
- // Serial.print(zmtrigarray);
- //}
- if (client.available()){
-
- for(int x=0;x<40;x++){
- //there are two UIPClient.ccp client.read functions. this is the 2nd
- zmtrigarray[x] = client.read();
- if(zmtrigarray[x] == 0b00001010){ //if line break is found in telnet
- y = x;
- goto PRINT;
- }
- }
- PRINT:
-
- uint8_t WhereInTheWorldIsThe1stLine = 0;
- uint8_t WhereInTheWorldIsThe2ndLine = 0;
-
- //Serial.println("Finished reading client");
- if(DEBUGMODE){
- for(int x=0;x<y;x++){
- Serial.print((char)zmtrigarray[x]);
- }
- Serial.println(F(""));
- //debugging
- for(int x=0;x<y;x++){
- Serial.print(F("x is: "));
- Serial.print(x);
- Serial.print(F(" val is: "));
- Serial.print((char)zmtrigarray[x]);
- Serial.print(F(" bytecode is: "));
- Serial.print((char)zmtrigarray[x], DEC); //print int rep of byte
- Serial.print(F(" binary is: "));
- Serial.print((char)zmtrigarray[x], BIN); //print int rep of byte
- //line break is 10 in telnet
- Serial.println(F(""));
- }
- Serial.println(F(""));
- }//end DEBUG
-
- //Find out monitor we have
- //
- if(zmtrigarray[1] == 0b01111100) { //looking for vertical dash, in spot 2 (1 of array)
- Mon1 = zmtrigarray[0];
- Mon1Nib = Mon1 & mask;
- MonFIN = Mon1Nib;
- WhereInTheWorldIsThe1stLine = 1;
- if(DEBUGMODE){
- Serial.print(F("Mon1 is: "));
- Serial.println(Mon1Nib, BIN);
- Serial.print(F("Monitor number is: "));
- Serial.println(MonFIN, DEC);
- Serial.println(MonFIN, BIN);
- }
- }
-
- if(zmtrigarray[1] != 0b01111100){ //looking for vertical dash, for two digit number
- Mon1 = zmtrigarray[0];
- Mon1Nib = Mon1 & mask; //convert from ascii to decimal
- WhereInTheWorldIsThe1stLine = 2;
- if(DEBUGMODE){
- Serial.print(F("Mon1 is: "));
- Serial.println(Mon1Nib, BIN);
- }
- Mon2 = zmtrigarray[1];
- Mon2Nib = Mon2 & mask; //convert from ascii to decimal
- if(DEBUGMODE){
- Serial.print(F("Mon2 is: "));
- Serial.println(Mon2Nib, BIN);
- }
-
- MonFIN = Mon1Nib & Mon2Nib;
- char MonBUF[2];
- sprintf(MonBUF,"%d%d", Mon1Nib,Mon2Nib);
- MonFIN = atoi(MonBUF); //To convert from Ascii to dec, we take the lower 4 bits,
- //convert to a string, then convert to int.
-
- if(DEBUGMODE){
- Serial.print(F("Monitor number is: "));
- Serial.println(MonFIN, DEC);
- Serial.println(MonFIN, BIN);
- }
- }//end vertical dash search (when in spot 3)
-
- //Looking for an N or an F (for ON or OFF)
- //possible values: On, Off
- char OnOffSwitch = 0;
- OnOffSwitch = zmtrigarray[WhereInTheWorldIsThe1stLine+2];
- if(DEBUGMODE){
- Serial.print(F("On/Off Switch is: "));
- Serial.println(OnOffSwitch);
- }
-
-
- if(OnOffSwitch == on){
-
-
- //Now, we block SoftPWM, in case LED was dimmed
- BypassSoftPWM[MonFIN] = 0;
- //Reset this, in case LED was dimmed
- onInterval[MonFIN] = LEDTIME;
-
- OffOverride[MonFIN] = 0;
- OffCtr[MonFIN] = 0;
-
- if (DEBUGMODE){
- Serial.print("Bypass soft pwm value is:");
- Serial.println(BypassSoftPWM[MonFIN], DEC);
-
- Serial.print(F("On Detected for Monitor: "));
- Serial.println(MonFIN, DEC);
- }
- value = (MonFIN + ADD);
-
- //minus 1 as array starts at 0
- //BypassSoftPWM[MonFIN - 1] = 1;
- //Serial.print("Bypass soft pwm value is:");
- //Serial.println(BypassSoftPWM[MonFIN - 1], DEC);
- LEDAlight(value, 0xFF);
-
- //Visual notification alarm has been off lately
- sendPixel(20,20,20);
-
-
- }
- else if(OnOffSwitch == off){
-
- if (DEBUGMODE){
- Serial.print(F("Off Detected for Monitor: "));
- Serial.println(MonFIN, DEC);
- }
- value = (MonFIN + ADD);
-
- //Start watchdog timer to shutdown leds that dont stop
- OffOverride[MonFIN] = 1;
-
-
- //Now, we allow SoftPWM, as LED was just turned off.
- //minus 1 as array starts at 0
- BypassSoftPWM[MonFIN] = 1;
- if (DEBUGMODE){
- Serial.print(F("Bypass soft pwm value is:"));
- Serial.println(BypassSoftPWM[MonFIN], DEC);
- }
-
- LEDAlight(value, 0x00); //turn led off
-
-
- //SoftPWM_LED_dim(value, Interval, MonFIN);
-
- }
- else if (OnOffSwitch != on && OnOffSwitch != off){
- Serial.println(F("Incorrect On/Off Value"));
- }
- }
-
-
-
-
- //WebServer(); //todo
-
-
- // as long as there are bytes in the serial queue,
- // read them and send them out the socket if it's open:
- while (Serial.available() > 0) {
- char inChar = Serial.read();
- if (client.connected()) {
- client.print(inChar);
-
- }
- }
-
- // if the server's disconnected, stop the client:
- if (!client.connected()) {
- Serial.println();
- Serial.println("disconnecting.");
- client.stop();
- // do nothing:
- //while (true);
-
- //GREEN to RED Error
- for(int err = 0; err<9;err++){
- sendPixel(10,0,0);
- delay(100);
- sendPixel(0,10,0);
- delay(100);
- }
-
- //start over
- setup();
- }
-
-
-
-
-
-
-
-
- }
-
- uint8_t LEDAlight (int Monitor, int Switch){
- //Enable or Disable GPIO based on Monitor
- //and Switch value
-
- digitalWrite(Monitor, Switch);
- if (DEBUGMODE){
- Serial.print(F("LED # has been switched: "));
- Serial.print(Monitor);
- Serial.print(F(" "));
- }
- if(Switch == 0xFF){
- if (DEBUGMODE){
- Serial.println(F(" On"));
- }
- }
- else{
- if (DEBUGMODE){
- Serial.println(F(" Off"));
- }
- }
- }
-
- //This will fade out the LED
- uint8_t SoftPWM_LED_dim (uint8_t Monbitbangpwm, uint16_t *ptr, uint8_t ptrarraynum){
-
-
- //arrays start at 0
- ptrarraynum = ptrarraynum - 1;
-
-
- if(DEBUGMODE){
- //check I did my pointers right...
- Serial.println(F("SoftPWM: ...."));
- Serial.print(F("Mega Pin is:"));
- Serial.println(Monbitbangpwm);
- int TempMon = Monbitbangpwm - ADD;
- Serial.print(F("Monitor is:"));
- Serial.println(TempMon);
- Serial.print(F("Interval from within softPWM is:"));
- //It's easier to understand, if you call it pass reference to pointer, not pass by reference to pointer.
- //aka pointer can pass value (the value of the data, but not the original data), or pass reference (a reference to the original data)
- //that little "by" word, makes things much more incomprehensible.
- for( int x = 0 ; x <= 50 ; x++ ){
- Serial.println( ptr[x], HEX );
- //ptr[x] = ptr[x] + 1; //looks like by default arrays pass a reference to array
- //no need for ptr magic
- // https://forum.arduino.cc/index.php?topic=42546.0
- }
- }
- if(DEBUGMODE)
- {
- Serial.println( ptr[ptrarraynum], DEC );
- //Now start LED dimming timer
- //ptr[ptrarraynum] = 200;
- //Serial.println( "now 200: ");
- //Serial.println( ptr[ptrarraynum], DEC );
- }
- //start LED dimming timer
- ptr[ptrarraynum] = 2000;
- Serial.println( ptr[ptrarraynum], DEC );
- Serial.println( ptrarraynum, DEC );
- Serial.println( "Is now at what was above the num.");
-
- }
-
-
- //interrupt 15hz
- //from https://www.arduinoslovakia.eu/application/timer-calculator
- //would be nice if it was downloadable...
-
- void setupTimer3() {
- noInterrupts();
- // Clear registers
- TCCR3A = 0;
- TCCR3B = 0;
- TCNT3 = 0;
-
- /*
- // 15.00060002400096 Hz (16000000/((16665+1)*64))
- OCR3A = 16665;
- // CTC
- TCCR3B |= (1 << WGM32);
- // Prescaler 64
- TCCR3B |= (1 << CS31) | (1 << CS30);
- // Output Compare Match A Interrupt Enable
- TIMSK3 |= (1 << OCIE3A);
- */
-
- /*
- // 60.00060000600006 Hz (16000000/((33332+1)*8))
- OCR3A = 33332;
- // CTC
- TCCR3B |= (1 << WGM32);
- // Prescaler 8
- TCCR3B |= (1 << CS31);
- // Output Compare Match A Interrupt Enable
- TIMSK3 |= (1 << OCIE3A);
- */
-
- // 240.00960038401536 Hz (16000000/((8332+1)*8))
- OCR3A = 8332;
- // CTC
- TCCR3B |= (1 << WGM32);
- // Prescaler 8
- TCCR3B |= (1 << CS31);
- // Output Compare Match A Interrupt Enable
- TIMSK3 |= (1 << OCIE3A);
-
- interrupts();
- }
-
- //using this to count 1hz, in case any buggy reports from telnet
- //sometimes i see a lot of monitors light up, then immediately turn off
- //on telnet, so accounting for that.
- void setupTimer4() {
- noInterrupts();
- // Clear registers
- TCCR4A = 0;
- TCCR4B = 0;
- TCNT4 = 0;
-
- // 1 Hz (16000000/((15624+1)*1024))
- OCR4A = 15624;
- // CTC
- TCCR4B |= (1 << WGM42);
- // Prescaler 1024
- TCCR4B |= (1 << CS42) | (1 << CS40);
- // Output Compare Match A Interrupt Enable
- TIMSK4 |= (1 << OCIE4A);
- interrupts();
- }
-
-
-
- void WebServer (void){
- /********************SERVER STATUS PAGE*********************/
- /*
- * With this, you can logon to the Sensor from your LAN to find
- * out just what device this IP address is, in case you happen to
- * forget.
- */
-
- //Serial.println("In server");
- //Server Status Page
- // listen for incoming clients
- EthernetClient client3 = server2.available();
- if (client3) {
- Serial.println(F("web visitor"));
- // an http request ends with a blank line
- boolean currentLineIsBlank = true;
- while (client3.connected()) {
- if (client3.available()) {
- char c = client3.read();
- Serial.write(c);
- // if you've gotten to the end of the line (received a newline
- // character) and the line is blank, the http request has ended,
- // so you can send a reply
- if (c == '\n' && currentLineIsBlank) {
- // send a standard http response header
- client3.println("HTTP/1.1 200 OK");
- client3.println("Content-Type: text/html");
- client3.println();
- client3.println("<!DOCTYPE HTML>");
- client3.println("<html><pre>");
- client3.println("<b>Steak Electronics</b>");
- client3.println("\"Steak it One Steak at a time.\"");
- client3.println("");
- //client.println("<b>IP Address:</b>");
- //client.println(Ethernet.localIP());
- //client.print("Sensor Location:");
- //client.println(LOCATIONOFSENSOR);
- client3.print("Type:");
- client3.println("ZMHW Map");
- client3.print("Last Activity was Monitor: ");
- client3.println(MonFIN);
- client3.println("</pre></html>");
- break;
- }
- if (c == '\n') {
- // you're starting a new line
- currentLineIsBlank = true;
- } else if (c != '\r') {
- // you've gotten a character on the current line
- currentLineIsBlank = false;
- }
- }
- }
- // give the web browser time to receive the data
- delay(1);
- // close the connection:
- client.stop();
- Serial.println(F("web visitor disconnected"));
- }
- }
-
- void PixZero (void){
- PORTE = 0b00010000;
- //_delay_us(0.25);
- PORTE = 0x00;
- //_delay_us(0.25);
- }
- void PixOne (void){
- PORTE = 0b00010000;
- //_delay_us(0.55);
- _delay_us(0.55);
- PORTE = 0x00;
- }
- void PixBit (bool res){
-
- if (res == false){
- PixZero();
- }
- if (res == true){
- PixOne();
- }
-
- }
-
- void PixByte (char input){
- uint8_t changer = 0;
-
- //WS2812 reads bits as left is lowest (high in first), so go backwards
- for(changer=8;changer>0;changer--){
- PixBit(bitRead(input, changer));
- //input <<= 1; //Atmega didn't like this, so instead, using changer
- //instead of shifting input
- }
- }
-
- void sendPixel(uint8_t g, uint8_t r, uint8_t b){
-
- /*ws2812, reads bits left side as lowest*/
- /*PixByte(0b10100000); //This is dim green
- PixByte(0b00000000);
- PixByte(0b00000000);
- PixByte(0b00000000); //no white on my LED*/
-
-
- PixByte(g);
- PixByte(r);
- PixByte(b);
- //PixByte(w);
- PORTE = 0x00;
-
-
- }
- void PulseRGB_g (uint8_t max, uint8_t r, uint8_t b){
- int rgbfunc = 0;
- for (rgbfunc = 0; rgbfunc < max; rgbfunc++){
- sendPixel(rgbfunc,r,b);
- delay(22);
- }
- for (rgbfunc = max; rgbfunc > 0; rgbfunc--){
- sendPixel(rgbfunc,r,b);
- delay(22);
- }
- }
- void PulseRGB_r (uint8_t max, uint8_t g, uint8_t b){
- int rgbfunc = 0;
- for (rgbfunc = 0; rgbfunc < max; rgbfunc++){
- sendPixel(g,rgbfunc, b);
- delay(22);
- }
- for (rgbfunc = max; rgbfunc > 0; rgbfunc--){
- sendPixel(g,rgbfunc, b);
- delay(22);
- }
- }
- void PulseRGB_b (uint8_t max, uint8_t g, uint8_t r){
- int rgbfunc = 0;
- for (rgbfunc = 0; rgbfunc < max; rgbfunc++){
- sendPixel(g,r,rgbfunc);
- delay(22);
- }
- for (rgbfunc = max; rgbfunc > 0; rgbfunc--){
- sendPixel(g,r,rgbfunc);
- delay(22);
- }
- }
-
-
-
-
-
|