This is an example of BLE GATT Client, which receives broadcast data from BLE_Server_BME280 ( a GATT server) , then transfers values up to mbed Device Connector (cloud).

Please refer details about BLEClient_mbedDevConn below. https://github.com/soramame21/BLEClient_mbedDevConn

The location of required BLE GATT server, BLE_Server_BME280, is at here. https://developer.mbed.org/users/edamame22/code/BLE_Server_BME280/

Committer:
Ren Boting
Date:
Tue Sep 05 11:56:13 2017 +0900
Revision:
2:b894b3508057
Parent:
0:29983394c6b6
Update all libraries and reform main.cpp

Who changed what in which revision?

UserRevisionLine numberNew contents of line
edamame22 0:29983394c6b6 1 # -----------------------------------------------------------------------
edamame22 0:29983394c6b6 2 # Copyright (c) 2016 ARM Limited. All rights reserved.
edamame22 0:29983394c6b6 3 # SPDX-License-Identifier: Apache-2.0
edamame22 0:29983394c6b6 4 # Licensed under the Apache License, Version 2.0 (the License); you may
edamame22 0:29983394c6b6 5 # not use this file except in compliance with the License.
edamame22 0:29983394c6b6 6 # You may obtain a copy of the License at
edamame22 0:29983394c6b6 7 #
edamame22 0:29983394c6b6 8 # http://www.apache.org/licenses/LICENSE-2.0
edamame22 0:29983394c6b6 9 #
edamame22 0:29983394c6b6 10 # Unless required by applicable law or agreed to in writing, software
edamame22 0:29983394c6b6 11 # distributed under the License is distributed on an AS IS BASIS, WITHOUT
edamame22 0:29983394c6b6 12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
edamame22 0:29983394c6b6 13 # See the License for the specific language governing permissions and
edamame22 0:29983394c6b6 14 # limitations under the License.
edamame22 0:29983394c6b6 15 # -----------------------------------------------------------------------
edamame22 0:29983394c6b6 16
edamame22 0:29983394c6b6 17 import os
edamame22 0:29983394c6b6 18 import shutil
edamame22 0:29983394c6b6 19 from time import sleep, strftime
edamame22 0:29983394c6b6 20 import serial
edamame22 0:29983394c6b6 21 import re
edamame22 0:29983394c6b6 22 import sys
edamame22 0:29983394c6b6 23
edamame22 0:29983394c6b6 24
edamame22 0:29983394c6b6 25 class MbedDeviceManager:
edamame22 0:29983394c6b6 26 def __init__(self):
edamame22 0:29983394c6b6 27 try:
edamame22 0:29983394c6b6 28 import mbed_lstools
edamame22 0:29983394c6b6 29 except ImportError, e:
edamame22 0:29983394c6b6 30 print("Error: Can't import 'mbed_lstools' module: %s"% e)
edamame22 0:29983394c6b6 31 mbed = mbed_lstools.create()
edamame22 0:29983394c6b6 32 self.mbed_list = mbed.list_mbeds()
edamame22 0:29983394c6b6 33 for dev in self.mbed_list:
edamame22 0:29983394c6b6 34 dev['Available'] = True
edamame22 0:29983394c6b6 35 # part. structure:{'mount_point': 'D:','serial_port': u'COM3','platform_name': 'LPC1768','Available'=True}
edamame22 0:29983394c6b6 36
edamame22 0:29983394c6b6 37 def dump_all_devices(self):
edamame22 0:29983394c6b6 38 print("dump Devices:")
edamame22 0:29983394c6b6 39 print(self.mbed_list)
edamame22 0:29983394c6b6 40
edamame22 0:29983394c6b6 41
edamame22 0:29983394c6b6 42 def get_device(self, platform_type):
edamame22 0:29983394c6b6 43 found = False
edamame22 0:29983394c6b6 44 index = int(-1)
edamame22 0:29983394c6b6 45 mount_point = None
edamame22 0:29983394c6b6 46 serial_port = None
edamame22 0:29983394c6b6 47 print(platform_type)
edamame22 0:29983394c6b6 48 for idx,dev in enumerate(self.mbed_list):
edamame22 0:29983394c6b6 49 if dev['platform_name'] == platform_type:
edamame22 0:29983394c6b6 50 found = True
edamame22 0:29983394c6b6 51 if dev['platform_name'] == platform_type and dev['Available']:
edamame22 0:29983394c6b6 52 mount_point = dev['mount_point']
edamame22 0:29983394c6b6 53 serial_port = dev['serial_port']
edamame22 0:29983394c6b6 54 print('Detected %s. Mount point %s Serial port %s index %s.'
edamame22 0:29983394c6b6 55 % (platform_type, mount_point, serial_port, idx))
edamame22 0:29983394c6b6 56 dev['Available'] = False
edamame22 0:29983394c6b6 57 return idx, mount_point, serial_port
edamame22 0:29983394c6b6 58 if found:
edamame22 0:29983394c6b6 59 print('Device %s is not available already in use.' %platform_type)
edamame22 0:29983394c6b6 60 else:
edamame22 0:29983394c6b6 61 print('Device %s is not found.' %platform_type)
edamame22 0:29983394c6b6 62 return index, mount_point, serial_port
edamame22 0:29983394c6b6 63
edamame22 0:29983394c6b6 64 def free_device(self, index):
edamame22 0:29983394c6b6 65 dev = self.mbed_list[index]
edamame22 0:29983394c6b6 66 if not dev['Available']:
edamame22 0:29983394c6b6 67 dev['Available'] = True
edamame22 0:29983394c6b6 68 print('Release Device %s : %s .' % (index, dev['platform_name']))
edamame22 0:29983394c6b6 69 else:
edamame22 0:29983394c6b6 70 print('Error: Device %s : %s is already free.' % (index, dev['platform_name']), )
edamame22 0:29983394c6b6 71
edamame22 0:29983394c6b6 72 mbed_manager = MbedDeviceManager()
edamame22 0:29983394c6b6 73
edamame22 0:29983394c6b6 74 class MBED:
edamame22 0:29983394c6b6 75 def __init__(self,platform_type):
edamame22 0:29983394c6b6 76 self.manager = mbed_manager
edamame22 0:29983394c6b6 77 self.platform_type = platform_type
edamame22 0:29983394c6b6 78 self.mount_point = None
edamame22 0:29983394c6b6 79 self.serial_port = None
edamame22 0:29983394c6b6 80 self.index = -1
edamame22 0:29983394c6b6 81 self.running = False
edamame22 0:29983394c6b6 82
edamame22 0:29983394c6b6 83 def detect(self):
edamame22 0:29983394c6b6 84 self.index, self.mount_point, self.serial_port = self.manager.get_device(self.platform_type)
edamame22 0:29983394c6b6 85 if self.index >= 0:
edamame22 0:29983394c6b6 86 return True
edamame22 0:29983394c6b6 87 else:
edamame22 0:29983394c6b6 88 return False
edamame22 0:29983394c6b6 89
edamame22 0:29983394c6b6 90 def generic_mbed_copy_win(self, source, destination):
edamame22 0:29983394c6b6 91
edamame22 0:29983394c6b6 92
edamame22 0:29983394c6b6 93 from win32com.shell import shell, shellcon
edamame22 0:29983394c6b6 94 import pythoncom
edamame22 0:29983394c6b6 95 from os.path import abspath, join
edamame22 0:29983394c6b6 96 from glob import glob
edamame22 0:29983394c6b6 97
edamame22 0:29983394c6b6 98 for f in glob(join(destination, '*.bin')):
edamame22 0:29983394c6b6 99 os.unlink(f)
edamame22 0:29983394c6b6 100
edamame22 0:29983394c6b6 101 src = shell.SHCreateItemFromParsingName(source, None, shell.IID_IShellItem)
edamame22 0:29983394c6b6 102 dest_dir = shell.SHCreateItemFromParsingName(
edamame22 0:29983394c6b6 103 abspath(destination),
edamame22 0:29983394c6b6 104 None,
edamame22 0:29983394c6b6 105 shell.IID_IShellItem
edamame22 0:29983394c6b6 106 )
edamame22 0:29983394c6b6 107 pfo = pythoncom.CoCreateInstance(
edamame22 0:29983394c6b6 108 shell.CLSID_FileOperation,
edamame22 0:29983394c6b6 109 None,
edamame22 0:29983394c6b6 110 pythoncom.CLSCTX_ALL,
edamame22 0:29983394c6b6 111 shell.IID_IFileOperation
edamame22 0:29983394c6b6 112 )
edamame22 0:29983394c6b6 113 pfo.SetOperationFlags(shellcon.FOF_NO_UI)
edamame22 0:29983394c6b6 114 pfo.CopyItem(src, dest_dir, None, None)
edamame22 0:29983394c6b6 115 pfo.PerformOperations()
edamame22 0:29983394c6b6 116
edamame22 0:29983394c6b6 117 return True
edamame22 0:29983394c6b6 118
edamame22 0:29983394c6b6 119 def install_bin(self,bin_file_name):
edamame22 0:29983394c6b6 120 bin_on_mbed = os.path.join(self.mount_point,os.path.basename(bin_file_name))
edamame22 0:29983394c6b6 121 print('%s Copying %s --> %s' %(strftime('%H:%M:%S'),bin_file_name,bin_on_mbed))
edamame22 0:29983394c6b6 122 if 'win' in sys.platform:
edamame22 0:29983394c6b6 123 self.generic_mbed_copy_win(os.path.abspath(bin_file_name), os.path.abspath(self.mount_point))
edamame22 0:29983394c6b6 124 else:
edamame22 0:29983394c6b6 125 shutil.copyfile(bin_file_name,bin_on_mbed)
edamame22 0:29983394c6b6 126 self.wait_for_file_system()
edamame22 0:29983394c6b6 127 print('%s Bin installation complete' %strftime('%H:%M:%S'))
edamame22 0:29983394c6b6 128
edamame22 0:29983394c6b6 129 def run(self,serial_capture_file_name,baud=9600,read_timeout=10,stop_on_serial_read_timeout=False):
edamame22 0:29983394c6b6 130 if serial_capture_file_name is not None:
edamame22 0:29983394c6b6 131 self.stop_on_serial_read_timeout = stop_on_serial_read_timeout
edamame22 0:29983394c6b6 132 from multiprocessing import Process, Event, Pipe
edamame22 0:29983394c6b6 133 self.event = Event()
edamame22 0:29983394c6b6 134 parent_conn, child_conn = Pipe()
edamame22 0:29983394c6b6 135 print('Start serial capture process')
edamame22 0:29983394c6b6 136 process_args=(self.serial_port,baud,read_timeout,serial_capture_file_name,stop_on_serial_read_timeout,self.event,child_conn)
edamame22 0:29983394c6b6 137 self.capture_process = Process(target=capture_serial,args=process_args)
edamame22 0:29983394c6b6 138 self.capture_process.start()
edamame22 0:29983394c6b6 139 print('Waiting for pipe input from subprocess')
edamame22 0:29983394c6b6 140 self.ip,self.port = parent_conn.recv()
edamame22 0:29983394c6b6 141 if not self.port or not self.ip :
edamame22 0:29983394c6b6 142 return 1
edamame22 0:29983394c6b6 143
edamame22 0:29983394c6b6 144 print('Received IP %s port %s'%(self.ip,self.port))
edamame22 0:29983394c6b6 145 else:
edamame22 0:29983394c6b6 146 print('Running without serial capture')
edamame22 0:29983394c6b6 147 self.ip,self.port = run_no_capture(self.serial_port,baud,read_timeout)
edamame22 0:29983394c6b6 148 print('%s IP %s port %s' % (self.platform_type, self.ip, self.port))
edamame22 0:29983394c6b6 149 self.running = True
edamame22 0:29983394c6b6 150
edamame22 0:29983394c6b6 151 def end_run(self, delete_binaries=True, release_manager=True):
edamame22 0:29983394c6b6 152 print('MBED end_run')
edamame22 0:29983394c6b6 153 if self.running:
edamame22 0:29983394c6b6 154 if not self.stop_on_serial_read_timeout:
edamame22 0:29983394c6b6 155 self.event.set() # the thread does not stop on its own. send stop signal
edamame22 0:29983394c6b6 156 print('MBED end_run waiting for subprocess to terminate')
edamame22 0:29983394c6b6 157 self.capture_process.join() # wait for completion
edamame22 0:29983394c6b6 158 print('MBED end_run subprocess terminated')
edamame22 0:29983394c6b6 159 if delete_binaries:
edamame22 0:29983394c6b6 160 print('MBED end_run deleting binaries')
edamame22 0:29983394c6b6 161 # delete all binaries on MBED
edamame22 0:29983394c6b6 162 filelist = [ f for f in os.listdir(self.mount_point) if f.endswith(".bin") ]
edamame22 0:29983394c6b6 163 for f in filelist:
edamame22 0:29983394c6b6 164 print('MBED end_run delete %s',f)
edamame22 0:29983394c6b6 165 os.remove(os.path.join(self.mount_point,f))
edamame22 0:29983394c6b6 166 self.wait_for_file_system()
edamame22 0:29983394c6b6 167 print('MBED end_run binary delete completed')
edamame22 0:29983394c6b6 168 self.running = False
edamame22 0:29983394c6b6 169 if release_manager:
edamame22 0:29983394c6b6 170 self.manager.free_device(self.index)
edamame22 0:29983394c6b6 171
edamame22 0:29983394c6b6 172 def run_and_capture_till_timeout(self,serial_capture_file_name,baud=9600,read_timeout=10,endOfData=None):
edamame22 0:29983394c6b6 173 try:
edamame22 0:29983394c6b6 174 print('[%s run_and_capture_till_timeout] Start' %strftime('%H:%M:%S'))
edamame22 0:29983394c6b6 175 ser = serial.Serial(self.serial_port,baudrate=baud,timeout=read_timeout)
edamame22 0:29983394c6b6 176 if ser == None:
edamame22 0:29983394c6b6 177 print(' serial.Serial returned None..')
edamame22 0:29983394c6b6 178 capture_file = open(serial_capture_file_name,'w')
edamame22 0:29983394c6b6 179 read_size = 1000
edamame22 0:29983394c6b6 180 cont = True
edamame22 0:29983394c6b6 181 print('[%s run_and_capture_till_timeout] Reseting device..' %strftime('%H:%M:%S'))
edamame22 0:29983394c6b6 182 c = reset_mbed(ser)
edamame22 0:29983394c6b6 183 if c == None:
edamame22 0:29983394c6b6 184 cont = False
edamame22 0:29983394c6b6 185 else:
edamame22 0:29983394c6b6 186 capture_file.write(c)
edamame22 0:29983394c6b6 187 print('[%s run_and_capture_till_timeout] capturing to file...' %strftime('%H:%M:%S'))
edamame22 0:29983394c6b6 188 while cont:
edamame22 0:29983394c6b6 189 c = ser.read(read_size)
edamame22 0:29983394c6b6 190 # Look for the end of test data string and terminate if it is found.
edamame22 0:29983394c6b6 191 if endOfData != None:
edamame22 0:29983394c6b6 192 endPos = c.find(endOfData)
edamame22 0:29983394c6b6 193 # Clip off the termination string and anything afterwards
edamame22 0:29983394c6b6 194 if endPos != -1:
edamame22 0:29983394c6b6 195 c = c[:(endPos + len(endOfData))]
edamame22 0:29983394c6b6 196 capture_file.write(c)
edamame22 0:29983394c6b6 197 if endPos != -1:
edamame22 0:29983394c6b6 198 break
edamame22 0:29983394c6b6 199 if len(c) < read_size:
edamame22 0:29983394c6b6 200 print('[%s run_and_capture_till_timeout] serial read timeout. Stopping subprocess' %strftime('%H:%M:%S'))
edamame22 0:29983394c6b6 201 print ("exit last Buffsize " + str(len(c)) + "<32" )
edamame22 0:29983394c6b6 202 cont = False
edamame22 0:29983394c6b6 203 print('[%s run_and_capture_till_timeout] closing capture file' %strftime('%H:%M:%S'))
edamame22 0:29983394c6b6 204 capture_file.close()
edamame22 0:29983394c6b6 205 print('[%s run_and_capture_till_timeout] closing serial port' %strftime('%H:%M:%S'))
edamame22 0:29983394c6b6 206 ser.flushInput()
edamame22 0:29983394c6b6 207 ser.flushOutput()
edamame22 0:29983394c6b6 208 ser.close()
edamame22 0:29983394c6b6 209 print('[%s run_and_capture_till_timeout] done' %strftime('%H:%M:%S'))
edamame22 0:29983394c6b6 210 return True
edamame22 0:29983394c6b6 211 except serial.SerialException as e:
edamame22 0:29983394c6b6 212 print('[run_and_capture_till_timeout] serial exception',e)
edamame22 0:29983394c6b6 213 return False
edamame22 0:29983394c6b6 214
edamame22 0:29983394c6b6 215 def wait_for_file_system(self):
edamame22 0:29983394c6b6 216 #sleep(25) #MBED file system takes some 'settling' time after it is wrirtten to
edamame22 0:29983394c6b6 217 sleep(3) #MBED file system takes some 'settling' time after it is wrirtten to
edamame22 0:29983394c6b6 218
edamame22 0:29983394c6b6 219 def reset_mbed(ser):
edamame22 0:29983394c6b6 220 for loop in range(5):
edamame22 0:29983394c6b6 221 # reset called after open port
edamame22 0:29983394c6b6 222 # add sleep for port ready (we saw that sometimes read failed...)
edamame22 0:29983394c6b6 223 delay = 5
edamame22 0:29983394c6b6 224 print('[%s reset_mbed] loop=%d , delay=%d' %(strftime('%H:%M:%S'), loop, delay))
edamame22 0:29983394c6b6 225 sleep(delay)
edamame22 0:29983394c6b6 226 try:
edamame22 0:29983394c6b6 227 ser.sendBreak()
edamame22 0:29983394c6b6 228 except Exception:
edamame22 0:29983394c6b6 229 # In linux a termios.error is raised in sendBreak and in
edamame22 0:29983394c6b6 230 # setBreak. The following setBreak() is needed to release
edamame22 0:29983394c6b6 231 # the reset signal on the target mcu.
edamame22 0:29983394c6b6 232 try:
edamame22 0:29983394c6b6 233 ser.setBreak(False)
edamame22 0:29983394c6b6 234 except:
edamame22 0:29983394c6b6 235 pass
edamame22 0:29983394c6b6 236 c = ser.read(1)
edamame22 0:29983394c6b6 237 if len(c) == 1:
edamame22 0:29983394c6b6 238 return c
edamame22 0:29983394c6b6 239 print ("Error reading from serial port" )
edamame22 0:29983394c6b6 240 return None
edamame22 0:29983394c6b6 241
edamame22 0:29983394c6b6 242 def is_valid_ip(ip):
edamame22 0:29983394c6b6 243 return re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$",ip)
edamame22 0:29983394c6b6 244
edamame22 0:29983394c6b6 245 def capture_serial(port,baud,read_timeout,capture_file_name,stop_on_serial_read_timeout,event,pipe_conn):
edamame22 0:29983394c6b6 246 try:
edamame22 0:29983394c6b6 247 print('[capture_serial subprocess] starting..')
edamame22 0:29983394c6b6 248 ser = serial.Serial(port,baudrate=baud,timeout=read_timeout)
edamame22 0:29983394c6b6 249 capture_file = open(capture_file_name,'w')
edamame22 0:29983394c6b6 250 read_size = 32
edamame22 0:29983394c6b6 251 cont = True
edamame22 0:29983394c6b6 252 print('[capture_serial subprocess] Reseting device..')
edamame22 0:29983394c6b6 253 c = reset_mbed(ser)
edamame22 0:29983394c6b6 254 if c == None:
edamame22 0:29983394c6b6 255 print('[capture_serial subprocess] Reseting device failed')
edamame22 0:29983394c6b6 256
edamame22 0:29983394c6b6 257 if c == None:
edamame22 0:29983394c6b6 258 cont = False
edamame22 0:29983394c6b6 259 print('[capture_serial subprocess] Failed to get IP from device... exiting ')
edamame22 0:29983394c6b6 260 pipe_conn.send([None,None]) # send IP and port
edamame22 0:29983394c6b6 261 pipe_conn.close()
edamame22 0:29983394c6b6 262 capture_file.close()
edamame22 0:29983394c6b6 263 ser.flushInput()
edamame22 0:29983394c6b6 264 ser.flushOutput()
edamame22 0:29983394c6b6 265 ser.close()
edamame22 0:29983394c6b6 266 return
edamame22 0:29983394c6b6 267 else:
edamame22 0:29983394c6b6 268 # parse the first received string for IP and port
edamame22 0:29983394c6b6 269 ip_str = c.split(':')
edamame22 0:29983394c6b6 270 if is_valid_ip(ip_str[1]) :
edamame22 0:29983394c6b6 271 pipe_conn.send([ip_str[1],ip_str[2]]) # send IP and port
edamame22 0:29983394c6b6 272 print('[capture_serial subprocess] Sending IP to main process %s:%s'%(ip_str[1],ip_str[2]))
edamame22 0:29983394c6b6 273 print('[capture_serial subprocess] capturing to file...')
edamame22 0:29983394c6b6 274 else:
edamame22 0:29983394c6b6 275 print('[capture_serial subprocess] No valid IP address')
edamame22 0:29983394c6b6 276 pipe_conn.send([None,None]) # send IP and port
edamame22 0:29983394c6b6 277 cont = False
edamame22 0:29983394c6b6 278 capture_file.write(c)
edamame22 0:29983394c6b6 279 pipe_conn.close()
edamame22 0:29983394c6b6 280
edamame22 0:29983394c6b6 281 while cont:
edamame22 0:29983394c6b6 282 c = ser.read(read_size)
edamame22 0:29983394c6b6 283 capture_file.write(c)
edamame22 0:29983394c6b6 284 if stop_on_serial_read_timeout:
edamame22 0:29983394c6b6 285 if len(c) < read_size:
edamame22 0:29983394c6b6 286 print('[capture_serial subprocess] serial read timeout. Stopping subprocess')
edamame22 0:29983394c6b6 287 cont = False
edamame22 0:29983394c6b6 288 else:
edamame22 0:29983394c6b6 289 if event.is_set():
edamame22 0:29983394c6b6 290 print('[capture_serial subprocess] event is set. Stopping subprocess')
edamame22 0:29983394c6b6 291 cont = False
edamame22 0:29983394c6b6 292
edamame22 0:29983394c6b6 293 print('[capture_serial subprocess] closing capture file')
edamame22 0:29983394c6b6 294 capture_file.close()
edamame22 0:29983394c6b6 295 print('[capture_serial subprocess] closing serial port')
edamame22 0:29983394c6b6 296 ser.flushInput()
edamame22 0:29983394c6b6 297 ser.flushOutput()
edamame22 0:29983394c6b6 298 ser.close()
edamame22 0:29983394c6b6 299 print('[capture_serial subprocess] Subprocess exiting')
edamame22 0:29983394c6b6 300 except serial.SerialException as e:
edamame22 0:29983394c6b6 301 print('[capture_serial subprocess] serial exception',e)
edamame22 0:29983394c6b6 302
edamame22 0:29983394c6b6 303 def run_no_capture(port,baud,read_timeout):
edamame22 0:29983394c6b6 304 try:
edamame22 0:29983394c6b6 305 ser = serial.Serial(port,baudrate=baud,timeout=read_timeout)
edamame22 0:29983394c6b6 306 ip = None
edamame22 0:29983394c6b6 307 port = None
edamame22 0:29983394c6b6 308 c = reset_mbed(ser)
edamame22 0:29983394c6b6 309 if c:
edamame22 0:29983394c6b6 310 # parse the first received string for IP and port
edamame22 0:29983394c6b6 311 ip_str = c.split(':')
edamame22 0:29983394c6b6 312 if is_valid_ip(ip_str[1]) != None:
edamame22 0:29983394c6b6 313 ip = ip_str[1]
edamame22 0:29983394c6b6 314 port = ip_str[2]
edamame22 0:29983394c6b6 315 ser.flushInput()
edamame22 0:29983394c6b6 316 ser.flushOutput()
edamame22 0:29983394c6b6 317 ser.close()
edamame22 0:29983394c6b6 318 return ip,port
edamame22 0:29983394c6b6 319 except serial.SerialException as e:
edamame22 0:29983394c6b6 320 print e
edamame22 0:29983394c6b6 321
edamame22 0:29983394c6b6 322
edamame22 0:29983394c6b6 323 class MBED_DEVICE(MBED):
edamame22 0:29983394c6b6 324 def __init__(self,platform_type):
edamame22 0:29983394c6b6 325 print('MBED Device:' + platform_type)
edamame22 0:29983394c6b6 326 MBED.__init__(self, platform_type)
edamame22 0:29983394c6b6 327
edamame22 0:29983394c6b6 328