project
Dependencies: mbed HTTPClient LM75B mbed-rtos EthernetInterface MMA7660
main.cpp@4:88613477855a, 2022-01-04 (annotated)
- Committer:
- rr387
- Date:
- Tue Jan 04 00:28:08 2022 +0000
- Revision:
- 4:88613477855a
- Parent:
- 3:e68b56ffa127
Project
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
rr387 | 3:e68b56ffa127 | 1 | /* mbed Microcontroller Library |
rr387 | 3:e68b56ffa127 | 2 | * Copyright (c) 2019 ARM Limited |
rr387 | 3:e68b56ffa127 | 3 | * SPDX-License-Identifier: Apache-2.0 |
rr387 | 3:e68b56ffa127 | 4 | */ |
rr387 | 3:e68b56ffa127 | 5 | #include "mbed.h" |
rr387 | 3:e68b56ffa127 | 6 | #include "FP.h"//header file for function pointer |
rr387 | 3:e68b56ffa127 | 7 | #include "HTTPClient.h" |
rr387 | 3:e68b56ffa127 | 8 | #include "EthernetInterface.h" |
rr387 | 3:e68b56ffa127 | 9 | |
rr387 | 3:e68b56ffa127 | 10 | |
rr387 | 3:e68b56ffa127 | 11 | #define TRIGGER_HAND PTB18//setting trigger pin of ultrasonic sensor near hand |
rr387 | 3:e68b56ffa127 | 12 | #define ECHO_HAND PTB19//setting echo pin of ultrasonic sensor near hand |
rr387 | 3:e68b56ffa127 | 13 | |
rr387 | 3:e68b56ffa127 | 14 | #define TRIGGER_BOTTLE PTC1//setting trigger pin of ultrasonic sensor inside bottle |
rr387 | 3:e68b56ffa127 | 15 | #define ECHO_BOTTLE PTC8//setting echo pin of ultrasonic sensor inside bottle |
rr387 | 3:e68b56ffa127 | 16 | |
rr387 | 3:e68b56ffa127 | 17 | #define PUMP_SIGNAL PTC9//stting the signal pin of the relay pin connected to pump |
rr387 | 3:e68b56ffa127 | 18 | |
rr387 | 3:e68b56ffa127 | 19 | #define BOTTLE_FULL 9// |
rr387 | 3:e68b56ffa127 | 20 | #define ON 1 |
rr387 | 3:e68b56ffa127 | 21 | #define OFF 0 |
rr387 | 3:e68b56ffa127 | 22 | Serial pc(USBTX, USBRX); //Create Serial interface |
rr387 | 3:e68b56ffa127 | 23 | DigitalOut RED(LED1, 1);//alert light when sanitizer is empty |
rr387 | 3:e68b56ffa127 | 24 | DigitalOut GREEN(LED2, 1);//user present station on |
rr387 | 3:e68b56ffa127 | 25 | DigitalOut BLUE(LED3, 1);//station free |
rr387 | 3:e68b56ffa127 | 26 | int hand_distance = 0;//variable to update feild 1 in thingsspeak |
rr387 | 3:e68b56ffa127 | 27 | int santizer_lev = 0;//variable to update feild 1 in thingsspeak |
rr387 | 3:e68b56ffa127 | 28 | long userCount = 0;//variable to update feild 1 in thingsspeak |
rr387 | 3:e68b56ffa127 | 29 | |
rr387 | 3:e68b56ffa127 | 30 | //Ethernet Interface |
rr387 | 3:e68b56ffa127 | 31 | EthernetInterface eth; |
rr387 | 3:e68b56ffa127 | 32 | //HTTP Client for interfacing to web services |
rr387 | 3:e68b56ffa127 | 33 | HTTPClient http; |
rr387 | 3:e68b56ffa127 | 34 | #define IP "184.106.153.149" // thingspeak.com IP Address |
rr387 | 3:e68b56ffa127 | 35 | |
rr387 | 3:e68b56ffa127 | 36 | char resp[1024];// response from http post |
rr387 | 3:e68b56ffa127 | 37 | int ret = 0;// error feed back from |
rr387 | 3:e68b56ffa127 | 38 | |
rr387 | 3:e68b56ffa127 | 39 | HTTPText inText(resp, 1024); |
rr387 | 3:e68b56ffa127 | 40 | |
rr387 | 3:e68b56ffa127 | 41 | void post_field1(){ |
rr387 | 3:e68b56ffa127 | 42 | HTTPMap map; |
rr387 | 3:e68b56ffa127 | 43 | char buf[10]; |
rr387 | 3:e68b56ffa127 | 44 | char buf1[10]; |
rr387 | 3:e68b56ffa127 | 45 | sprintf(buf,"%d",userCount);//Convert UserCount to char* |
rr387 | 3:e68b56ffa127 | 46 | sprintf(buf1,"%d",santizer_lev);//Convert SanitizerLevel to char* |
rr387 | 3:e68b56ffa127 | 47 | map.put("api_key","JPZYYS7J18NX4XV5"); //Fill your thingspeak API KEY |
rr387 | 3:e68b56ffa127 | 48 | map.put("field1", buf); // Add UserCount to HTTPMap |
rr387 | 3:e68b56ffa127 | 49 | map.put("field2", buf1);// Add SanitizerLevel to HTTPMap |
rr387 | 3:e68b56ffa127 | 50 | pc.printf("\r\nPosting Data..."); |
rr387 | 3:e68b56ffa127 | 51 | ret = http.post("https://api.thingspeak.com/update", map, &inText); //Publish Field1 and Field2 to Thingspeak |
rr387 | 3:e68b56ffa127 | 52 | if (!ret){ |
rr387 | 3:e68b56ffa127 | 53 | pc.printf("\r\nPOST successfully - read %d characters", strlen(resp)); |
rr387 | 3:e68b56ffa127 | 54 | pc.printf("\r\nResult: %s\n", resp); |
rr387 | 3:e68b56ffa127 | 55 | }else{ |
rr387 | 3:e68b56ffa127 | 56 | pc.printf("\r\nError Connecting to ethernet port - ret = %d - HTTP return code = %d", ret, http.getHTTPResponseCode()); |
rr387 | 3:e68b56ffa127 | 57 | } |
rr387 | 3:e68b56ffa127 | 58 | } |
rr387 | 3:e68b56ffa127 | 59 | |
rr387 | 3:e68b56ffa127 | 60 | void set_red()//funtion to set red led |
rr387 | 3:e68b56ffa127 | 61 | { |
rr387 | 3:e68b56ffa127 | 62 | RED=0; GREEN = 1; BLUE = 1; |
rr387 | 3:e68b56ffa127 | 63 | } |
rr387 | 3:e68b56ffa127 | 64 | void set_green()//funtion to set green led |
rr387 | 3:e68b56ffa127 | 65 | { |
rr387 | 3:e68b56ffa127 | 66 | RED = 1; GREEN = 0; BLUE = 1; |
rr387 | 3:e68b56ffa127 | 67 | } |
rr387 | 3:e68b56ffa127 | 68 | void set_blue()//funtion to set blue led |
rr387 | 3:e68b56ffa127 | 69 | { |
rr387 | 3:e68b56ffa127 | 70 | RED = 1; GREEN = 1; BLUE = 0; |
rr387 | 3:e68b56ffa127 | 71 | } |
rr387 | 3:e68b56ffa127 | 72 | |
rr387 | 3:e68b56ffa127 | 73 | class ultrasonic{ |
rr387 | 3:e68b56ffa127 | 74 | private: |
rr387 | 3:e68b56ffa127 | 75 | DigitalOut trigger;//trigger pin for UltarSonic sensor |
rr387 | 3:e68b56ffa127 | 76 | InterruptIn echo; //Echo pin for water_level measurement. Uses as Interrupt to avoid code execution blocking. |
rr387 | 3:e68b56ffa127 | 77 | Ticker measure; //Ticker to trigger water_level measurement periodically. |
rr387 | 3:e68b56ffa127 | 78 | Timeout trigger_low; //Timeout interrupt to set trigger pin low after 10us of HIGH. |
rr387 | 3:e68b56ffa127 | 79 | Timer echoTimer; //Timer to get the time elapsed between echo sent and echo received back |
rr387 | 3:e68b56ffa127 | 80 | int echo_time_us; |
rr387 | 3:e68b56ffa127 | 81 | int meas_gap; //1sec |
rr387 | 3:e68b56ffa127 | 82 | volatile bool measuring; |
rr387 | 3:e68b56ffa127 | 83 | FP<void,int> fp; |
rr387 | 3:e68b56ffa127 | 84 | |
rr387 | 3:e68b56ffa127 | 85 | void set_trigger_high(){ //Function to set Trigger Pin HIGH for 10us and set measuring = true |
rr387 | 3:e68b56ffa127 | 86 | measuring = true; |
rr387 | 3:e68b56ffa127 | 87 | trigger = ON; |
rr387 | 3:e68b56ffa127 | 88 | trigger_low.attach_us(this, &ultrasonic::set_trigger_low, 10); //set trigger pin OFF after 10us |
rr387 | 3:e68b56ffa127 | 89 | } |
rr387 | 3:e68b56ffa127 | 90 | |
rr387 | 3:e68b56ffa127 | 91 | void set_trigger_low(void){ //Function to set trigger pin LOW to fiish trigger after 10us. This will be called by low timer object. |
rr387 | 3:e68b56ffa127 | 92 | trigger = OFF; |
rr387 | 3:e68b56ffa127 | 93 | } |
rr387 | 3:e68b56ffa127 | 94 | |
rr387 | 3:e68b56ffa127 | 95 | void start_echo(void){//Function to start listening for echo back. This will start timer echoTimer. Will be based on Echo Pin RISE(or HIGH) |
rr387 | 3:e68b56ffa127 | 96 | if(!measuring) return; //avoid fluctuations on echo pin if any. Continue echo calculation only if measuring is TRUE. |
rr387 | 3:e68b56ffa127 | 97 | echoTimer.reset(); |
rr387 | 3:e68b56ffa127 | 98 | echoTimer.start(); //Timer start since Echo pin is HIGH |
rr387 | 3:e68b56ffa127 | 99 | } |
rr387 | 3:e68b56ffa127 | 100 | void stop_echo(void){//Function to stop echoTimer after echo received back. Will be based on Echo Pin FALL(or LOW). |
rr387 | 3:e68b56ffa127 | 101 | if (!measuring)return; //avoid fluctuations on echo pin if any |
rr387 | 3:e68b56ffa127 | 102 | echoTimer.stop(); //stop timer since echo pin back to LOW |
rr387 | 3:e68b56ffa127 | 103 | echo_time_us = echoTimer.read_us(); //Calculate time for ultrasonic wave RTT. |
rr387 | 3:e68b56ffa127 | 104 | if(echo_time_us > 2000 || echo_time_us < 125)return; //ignore reading in case abnormally high or low RTT time |
rr387 | 3:e68b56ffa127 | 105 | measuring = false; //Marke measurement completed |
rr387 | 3:e68b56ffa127 | 106 | fp(echo_time_us / (29 * 2)); |
rr387 | 3:e68b56ffa127 | 107 | } |
rr387 | 3:e68b56ffa127 | 108 | public: |
rr387 | 3:e68b56ffa127 | 109 | ultrasonic(PinName _trigger, PinName _echo, int _meas_gap):trigger(_trigger), echo(_echo){ //Constructor: initialize trigger(default value OFF) & echo pin |
rr387 | 3:e68b56ffa127 | 110 | |
rr387 | 3:e68b56ffa127 | 111 | echo.rise(this, &ultrasonic::start_echo); //Attach function to Interrupt on Echo pin Rise |
rr387 | 3:e68b56ffa127 | 112 | echo.fall(this, &ultrasonic::stop_echo); //Attach function to Interrupt on Echo pin Fall |
rr387 | 3:e68b56ffa127 | 113 | meas_gap = _meas_gap; |
rr387 | 3:e68b56ffa127 | 114 | measuring = false; |
rr387 | 3:e68b56ffa127 | 115 | } |
rr387 | 3:e68b56ffa127 | 116 | |
rr387 | 3:e68b56ffa127 | 117 | void start_measure(){ //function to Set Trigger Pin high repeatedly every 'time' seconds |
rr387 | 3:e68b56ffa127 | 118 | measure.attach(this, &ultrasonic::set_trigger_high,meas_gap);//ticker to trigger measurement with callback function |
rr387 | 3:e68b56ffa127 | 119 | } |
rr387 | 3:e68b56ffa127 | 120 | void stop_measure(void){ //Dettach from Ticker and stop measurements |
rr387 | 3:e68b56ffa127 | 121 | //signal = OFF; |
rr387 | 3:e68b56ffa127 | 122 | measuring = false; |
rr387 | 3:e68b56ffa127 | 123 | measure.detach(); |
rr387 | 3:e68b56ffa127 | 124 | } |
rr387 | 3:e68b56ffa127 | 125 | void req_callBack(FP<void, int> _fp){ |
rr387 | 3:e68b56ffa127 | 126 | fp = _fp; |
rr387 | 3:e68b56ffa127 | 127 | } |
rr387 | 3:e68b56ffa127 | 128 | }; |
rr387 | 3:e68b56ffa127 | 129 | |
rr387 | 3:e68b56ffa127 | 130 | |
rr387 | 3:e68b56ffa127 | 131 | class _pump{ |
rr387 | 3:e68b56ffa127 | 132 | private: |
rr387 | 3:e68b56ffa127 | 133 | DigitalOut signal; //pin for controlling relay |
rr387 | 3:e68b56ffa127 | 134 | const int pump_timeout = 400000;//the amount of sanitizer pumping into the hand |
rr387 | 3:e68b56ffa127 | 135 | Timeout time_out;//interrupt based timer |
rr387 | 3:e68b56ffa127 | 136 | |
rr387 | 3:e68b56ffa127 | 137 | public: |
rr387 | 3:e68b56ffa127 | 138 | _pump(PinName _signal):signal(_signal,OFF){ |
rr387 | 3:e68b56ffa127 | 139 | } |
rr387 | 3:e68b56ffa127 | 140 | |
rr387 | 3:e68b56ffa127 | 141 | void switch_on(){//switch on the punp |
rr387 | 3:e68b56ffa127 | 142 | signal = ON; |
rr387 | 3:e68b56ffa127 | 143 | time_out.attach_us(this, &_pump::switch_off, pump_timeout); //set trigger pin OFF after 10us |
rr387 | 3:e68b56ffa127 | 144 | } |
rr387 | 3:e68b56ffa127 | 145 | void switch_off(){//switch off the pump |
rr387 | 3:e68b56ffa127 | 146 | signal = OFF; |
rr387 | 3:e68b56ffa127 | 147 | } |
rr387 | 3:e68b56ffa127 | 148 | |
rr387 | 3:e68b56ffa127 | 149 | int get_state(){//return the signal state of pump |
rr387 | 3:e68b56ffa127 | 150 | return signal.read(); |
rr387 | 3:e68b56ffa127 | 151 | } |
rr387 | 3:e68b56ffa127 | 152 | }; |
rr387 | 3:e68b56ffa127 | 153 | |
rr387 | 3:e68b56ffa127 | 154 | class _station{ |
rr387 | 3:e68b56ffa127 | 155 | private: |
rr387 | 3:e68b56ffa127 | 156 | ultrasonic hand_sensor; |
rr387 | 3:e68b56ffa127 | 157 | ultrasonic bottle_sensor; |
rr387 | 3:e68b56ffa127 | 158 | _pump pump; |
rr387 | 3:e68b56ffa127 | 159 | FP<void,int> detect_hand_fp;//to find the user hand using function pointer detect hand fp |
rr387 | 3:e68b56ffa127 | 160 | FP<void,int> santizer_level_fp;//to find the level of sanitizer using this function |
rr387 | 3:e68b56ffa127 | 161 | const int threshold = 14;//the max value hand to be detected |
rr387 | 3:e68b56ffa127 | 162 | const int bottle_empty = 13;// the max value bottle level to get empty |
rr387 | 3:e68b56ffa127 | 163 | bool user_served;//variable to check the user served |
rr387 | 3:e68b56ffa127 | 164 | bool station_on;//varible to keep the sanitizing station on |
rr387 | 3:e68b56ffa127 | 165 | public: |
rr387 | 3:e68b56ffa127 | 166 | _station(PinName _trigger,PinName _echo, PinName __trigger,PinName __echo, PinName _signal):hand_sensor(_trigger, _echo, 1),bottle_sensor(__trigger, __echo, 10),pump(_signal){ |
rr387 | 3:e68b56ffa127 | 167 | |
rr387 | 3:e68b56ffa127 | 168 | bool user_served= false;//initialize user serve |
rr387 | 3:e68b56ffa127 | 169 | bool station_on = true;//initialize the station to start |
rr387 | 3:e68b56ffa127 | 170 | } |
rr387 | 3:e68b56ffa127 | 171 | |
rr387 | 3:e68b56ffa127 | 172 | void start_station(){//This section the station will check the hand and bottle distance detection |
rr387 | 3:e68b56ffa127 | 173 | start_measure_bottle();//start to measure the bottle sanitizer level |
rr387 | 3:e68b56ffa127 | 174 | } |
rr387 | 3:e68b56ffa127 | 175 | |
rr387 | 3:e68b56ffa127 | 176 | void start_detect_hand(){//start detecting the hand |
rr387 | 3:e68b56ffa127 | 177 | detect_hand_fp.attach(this, &_station::detect_hand);//attach the function pointer detect hand |
rr387 | 3:e68b56ffa127 | 178 | hand_sensor.req_callBack(detect_hand_fp);//pass function pointer to ultrasonic function |
rr387 | 3:e68b56ffa127 | 179 | hand_sensor.start_measure();//start hand sensor |
rr387 | 3:e68b56ffa127 | 180 | } |
rr387 | 3:e68b56ffa127 | 181 | void stop_detect_hand(){//stop detecting the hand |
rr387 | 3:e68b56ffa127 | 182 | hand_sensor.stop_measure();//stop ultrasonic sensor |
rr387 | 3:e68b56ffa127 | 183 | detect_hand_fp.detach();//detach the function pointer |
rr387 | 3:e68b56ffa127 | 184 | } |
rr387 | 3:e68b56ffa127 | 185 | |
rr387 | 3:e68b56ffa127 | 186 | void start_measure_bottle(){//start measuring the level of sanitizer |
rr387 | 3:e68b56ffa127 | 187 | santizer_level_fp.attach(this, &_station::santizer_level);//attach the function pointer sanitizer level |
rr387 | 3:e68b56ffa127 | 188 | bottle_sensor.req_callBack(santizer_level_fp);//pass function pointer to ultrasonic function |
rr387 | 3:e68b56ffa127 | 189 | bottle_sensor.start_measure();//start calculating level of sanitizer |
rr387 | 3:e68b56ffa127 | 190 | } |
rr387 | 3:e68b56ffa127 | 191 | void stop_measure_bottle(){//stop measuring the level |
rr387 | 3:e68b56ffa127 | 192 | stop_detect_hand();//stop detecting hand |
rr387 | 3:e68b56ffa127 | 193 | bottle_sensor.stop_measure();//stop the ultrasonic sensor |
rr387 | 3:e68b56ffa127 | 194 | santizer_level_fp.detach();//detach the function pointer |
rr387 | 3:e68b56ffa127 | 195 | } |
rr387 | 3:e68b56ffa127 | 196 | |
rr387 | 3:e68b56ffa127 | 197 | void santizer_level(int d){ |
rr387 | 3:e68b56ffa127 | 198 | //int x = d - BOTTLE_FULL; |
rr387 | 3:e68b56ffa127 | 199 | //santizer_lev = BOTTLE_FULL - x; |
rr387 | 3:e68b56ffa127 | 200 | santizer_lev = d; |
rr387 | 3:e68b56ffa127 | 201 | char field[50]; |
rr387 | 3:e68b56ffa127 | 202 | if (d <= bottle_empty){ //Bottle Empty |
rr387 | 3:e68b56ffa127 | 203 | set_red();//sanitizer empty red light alert |
rr387 | 3:e68b56ffa127 | 204 | station_on = false;//make the station off |
rr387 | 3:e68b56ffa127 | 205 | stop_detect_hand();//stop sensing the hand |
rr387 | 3:e68b56ffa127 | 206 | } |
rr387 | 3:e68b56ffa127 | 207 | if (d > bottle_empty){ //Bottle still have sanitizer |
rr387 | 3:e68b56ffa127 | 208 | set_blue();//sanitizer present and users are welcome |
rr387 | 3:e68b56ffa127 | 209 | if(!station_on){// if no user is using the station |
rr387 | 3:e68b56ffa127 | 210 | start_detect_hand();// start hand detection |
rr387 | 3:e68b56ffa127 | 211 | station_on = true;// on the station for pumping |
rr387 | 3:e68b56ffa127 | 212 | } |
rr387 | 3:e68b56ffa127 | 213 | } |
rr387 | 3:e68b56ffa127 | 214 | } |
rr387 | 3:e68b56ffa127 | 215 | void detect_hand(int d){//hand detection |
rr387 | 3:e68b56ffa127 | 216 | hand_distance = d; |
rr387 | 3:e68b56ffa127 | 217 | if (d <= threshold && user_served){ //hand Detected when user is served and the threshold is greater than the distance |
rr387 | 3:e68b56ffa127 | 218 | set_green();//green led glows when hand is detected |
rr387 | 3:e68b56ffa127 | 219 | pump.switch_on();//switch the pump on |
rr387 | 3:e68b56ffa127 | 220 | userCount++;//count the user number |
rr387 | 3:e68b56ffa127 | 221 | user_served= false;//stop the pumping after one pump of sanitizer |
rr387 | 3:e68b56ffa127 | 222 | |
rr387 | 3:e68b56ffa127 | 223 | } |
rr387 | 3:e68b56ffa127 | 224 | if (d > threshold){ //hand not Detected |
rr387 | 3:e68b56ffa127 | 225 | set_blue();//station is free ready to serve user |
rr387 | 3:e68b56ffa127 | 226 | user_served= true;//start checking for the user |
rr387 | 3:e68b56ffa127 | 227 | } |
rr387 | 3:e68b56ffa127 | 228 | } |
rr387 | 3:e68b56ffa127 | 229 | }; |
rr387 | 3:e68b56ffa127 | 230 | |
rr387 | 3:e68b56ffa127 | 231 | |
rr387 | 3:e68b56ffa127 | 232 | |
rr387 | 3:e68b56ffa127 | 233 | int main() |
rr387 | 3:e68b56ffa127 | 234 | { |
rr387 | 3:e68b56ffa127 | 235 | int ret = 0; |
rr387 | 3:e68b56ffa127 | 236 | char field[50]; |
rr387 | 4:88613477855a | 237 | _station station(TRIGGER_HAND, ECHO_HAND, TRIGGER_BOTTLE, ECHO_BOTTLE, PUMP_SIGNAL);//initialize the station with sensor pins |
rr387 | 4:88613477855a | 238 | station.start_station();//start the station |
rr387 | 3:e68b56ffa127 | 239 | |
rr387 | 4:88613477855a | 240 | eth.init(); //initialize ethernet port |
rr387 | 4:88613477855a | 241 | ret= eth.connect();//connect the ethernet |
rr387 | 3:e68b56ffa127 | 242 | |
rr387 | 4:88613477855a | 243 | if (!ret){//check if ethernet connected successfully or not |
rr387 | 3:e68b56ffa127 | 244 | pc.printf("\r\nConnected, IP: %s, MASK: %s, GW: %s", |
rr387 | 3:e68b56ffa127 | 245 | eth.getIPAddress(), eth.getNetworkMask(), eth.getGateway()); |
rr387 | 3:e68b56ffa127 | 246 | } else { |
rr387 | 3:e68b56ffa127 | 247 | pc.printf("\r\nError eth.connect() - ret = %d", ret); |
rr387 | 3:e68b56ffa127 | 248 | } |
rr387 | 3:e68b56ffa127 | 249 | |
rr387 | 3:e68b56ffa127 | 250 | |
rr387 | 3:e68b56ffa127 | 251 | while (true) { |
rr387 | 3:e68b56ffa127 | 252 | pc.printf("Hand Distance = %d\n\r", hand_distance);//print the hand distance calculated |
rr387 | 3:e68b56ffa127 | 253 | pc.printf("Sanitizer = %d\n\r", santizer_lev);//print the sanitizer level detected |
rr387 | 3:e68b56ffa127 | 254 | wait(5); |
rr387 | 4:88613477855a | 255 | post_field1();//calling the post feild1 to post the information to thingspaek |
rr387 | 3:e68b56ffa127 | 256 | |
rr387 | 3:e68b56ffa127 | 257 | } |
rr387 | 3:e68b56ffa127 | 258 | } |
rr387 | 3:e68b56ffa127 | 259 | |
rr387 | 3:e68b56ffa127 | 260 | |
rr387 | 3:e68b56ffa127 | 261 | |
rr387 | 3:e68b56ffa127 | 262 |