diff --git a/.circleci/config.yml b/.circleci/config.yml index 3d191acd..76de3154 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ jobs: build: docker: # specify the version you desire here - - image: coderbot/python-gpac:3.5-tf2 + - image: coderbot/coderbot-ci:3.9-bullseye-ffmpeg working_directory: ~/repo diff --git a/.gitignore b/.gitignore index ed0fd582..d59dc0e5 100644 --- a/.gitignore +++ b/.gitignore @@ -70,7 +70,6 @@ Thumbs.db # Swap files *.swp - # Python3 Virtual Environment folders bin/ @@ -94,3 +93,7 @@ photos/metadata.json # Uploaded updates folder updatePackages/ + +# firmware +firmware/ + diff --git a/README.md b/README.md index 9f21cece..b618a798 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # backend +[![CoderBotOrg](https://circleci.com/gh/CoderBotOrg/backend.svg?style=svg)](https://circleci.com/gh/CoderBotOrg/backend/tree/master) > CoderBot is a RaspberryPI-based programmable robot for educational purposes. Check the [project website](https://www.coderbot.org) for more information. > diff --git a/activity.py b/activity.py new file mode 100644 index 00000000..7c46ac88 --- /dev/null +++ b/activity.py @@ -0,0 +1,45 @@ +from tinydb import TinyDB, Query +from tinydb.operations import delete +import json + +# Programs and Activities databases +class Activities(): + _instance = None + + @classmethod + def get_instance(cls): + if cls._instance == None: + cls._instance = Activities() + return cls._instance + + def __init__(self): + self.activities = TinyDB("data/activities.json") + self.query = Query() + + def load(self, name, default): + if name: + return self.activities.search(self.query.name == name)[0] + elif default is not None: + default_Activities = self.activities.search(self.query.default == True) + if len(self.activities.search(self.query.default == True)) > 0: + return self.activities.search(self.query.default == True)[0] + else: + return None + + def save(self, activity): + if self.activities.search(self.query.name == activity["name"]) == []: + self.activities.insert(activity) + else: + if activity.get("default", False) == True: + self.activities.update({'default': False}) + self.activities.update(activity, self.query.name == activity["name"]) + + def delete(self, activity): + activity = self.activities.search(self.query.name == activity["name"])[0] + if activity.get("default", False) == True: + self.activities.update({'default': True}, self.query.stock == True) + self.activities.remove(self.query.name == activity["name"]) + + def list(self): + return self.activities.all() + diff --git a/api.py b/api.py index 08c22c1d..c03cf5b0 100644 --- a/api.py +++ b/api.py @@ -6,15 +6,17 @@ import os import subprocess import json +import logging import connexion -from tinydb import TinyDB, Query -from tinydb.operations import delete +import pigpio from cachetools import cached, TTLCache from coderbot import CoderBot from program import ProgramEngine, Program from config import Config +from activity import Activities from coderbotTestUnit import run_test as runCoderbotTestUnit -import pigpio +from cnn_manager import CNNManager +from musicPackages import MusicPackageManager BUTTON_PIN = 16 @@ -24,8 +26,6 @@ encoder=bool(bot_config.get("encoder")) ) -query = Query() - def get_serial(): """ Extract serial from cpuinfo file @@ -107,8 +107,7 @@ def get_info(): prog = None prog_engine = ProgramEngine.get_instance() -# Programs and Activities databases -activities = TinyDB("data/activities.json") +activities = Activities.get_instance() ## Robot control @@ -132,7 +131,8 @@ def turn(data): def exec(data): program = prog_engine.create(data["name"], data["code"]) - return json.dumps(program.execute()) + options = data["options"] + return json.dumps(program.execute(options)) ## System @@ -172,6 +172,7 @@ def restoreSettings(): Config.get() return "ok" + def updateFromPackage(): os.system('sudo bash /home/pi/clean-update.sh') file_to_upload = connexion.request.files['file_to_upload'] @@ -179,11 +180,47 @@ def updateFromPackage(): os.system('sudo reboot') return 200 +def listMusicPackages(): + """ + list available music packages + """ + musicPkg = MusicPackageManager.get_instance() + response = musicPkg.listPackages() + return json.dumps(response) +def updateMusicPackages(): + """ + Add a musical package an save the list of available packages on disk + also add sounds and directory + """ + """zipName = request.args.get("zipname") + """ + file_to_upload = connexion.request.files['file_to_upload'] + print("adding " +str(file_to_upload)) + print("adding " + file_to_upload.filename) + file_to_upload.save(os.path.join('./updatePackages/', file_to_upload.filename)) + musicPkg = MusicPackageManager.get_instance() + response = musicPkg.addPackage(file_to_upload.filename) + if response == 1: + return 200 + elif response == 2: + return 400 + elif response == 3: + return 400 + +def deleteMusicPackage(package_data): + """ + Delete a musical package an save the list of available packages on disk + also delete package sounds and directory + """ + musicPkg = MusicPackageManager.get_instance() + musicPkg.deletePackage(package_data['package_name']) + return 200 ## Programs -def saveProgram(data, overwrite): +def saveProgram(data): + overwrite = data["overwrite"] existing_program = prog_engine.load(data["name"]) if existing_program and not overwrite: return "askOverwrite" @@ -207,23 +244,17 @@ def listPrograms(): ## Activities def saveActivity(data): - data = data["activity"] - if activities.search(query.name == data["name"]) == []: - activities.insert(data) - return 200 - else: - activities.update(data, query.name == data["name"]) - return 200 + activity = data["activity"] + activities.save(activity) -def loadActivity(name): - return activities.search(query.name == name)[0], 200 +def loadActivity(name=None, default=None): + return activities.load(name, default) def deleteActivity(data): - activities.remove(query.name == data["name"]) - + activities.delete(data), 200 def listActivities(): - return activities.all() + return activities.list() def resetDefaultPrograms(): """ @@ -252,4 +283,10 @@ def reset(): def testCoderbot(data): # taking first JSON key value (varargin) tests_state = runCoderbotTestUnit(data[list(data.keys())[0]]) - return tests_state \ No newline at end of file + return tests_state + +def list_cnn_models(): + cnn = CNNManager.get_instance() + logging.info("cnn_models_list") + return json.dumps(cnn.get_models()) + diff --git a/atmega328p.py b/atmega328p.py new file mode 100644 index 00000000..76c3965b --- /dev/null +++ b/atmega328p.py @@ -0,0 +1,84 @@ +# RPi PINOUTS +# MOSI -> GPIO10 +# MISO -> GPIO9 +# SCK -> GPIO11 +# CE1 -> GPIO7 +# CE1 -> GPIO8 + +# get the GPIO Library and SPI Library +import spidev +import time + +BAUDRATE_MAX = 250000 +BAUDRATE = 10000 + +START = 0xff +CMD_RESET = 0x00 +CMD_SET_DATA = 0x01 +CMD_GET_DATA = 0x02 +CMD_SET_MODE = 0x03 +CMD_SET_LED = 0x04 + +ADDR_AI_FIRST = 0x00 +ADDR_AI_LAST = 0x01 +ADDR_DI_FIRST = 0x02 +ADDR_DI_LAST = 0x05 +ADDR_DO_FIRST = 0x00 +ADDR_DO_LAST = 0x0a + +class ATMega328(): + + _instance = None + + @classmethod + def get_instance(cls): + if cls._instance is None: + cls._instance = ATMega328() + return cls._instance + + def __init__(self): + # Initialze the SPI + self.spi = spidev.SpiDev() + self.spi.open(0,0) + self.spi.max_speed_hz = BAUDRATE_MAX + + def close(self): + self.spi.close() + + def digitalWrite(self, addr, value): + resp = self.spi.xfer([START, CMD_SET_DATA, addr, value, 0], BAUDRATE) + + def digitalRead(self, addr): + resp = self.spi.xfer([START, CMD_GET_DATA, addr, 0, 0], BAUDRATE) + return resp[3] + + def analogRead(self, addr): + resp = self.spi.xfer([START, CMD_GET_DATA, addr, 0, 0], BAUDRATE) + return resp[3] + + def setLed(self, begin_led, end_led, red, green, blue): + resp = self.spi.xfer([START, CMD_SET_LED, + min(max(begin_led, 0), 60), + min(max(end_led, 0), 60), + min(max(red, 0), 254), + min(max(green, 0), 254), + min(max(blue, 0), 254)], BAUDRATE) + return resp[3] + + def set_led(self, begin_led, end_led, red, green, blue): + begin = begin_led - 1 + end = end_led - 1 + red = int(red * 255 / 100) + green = int(green * 255 / 100) + blue = int(blue * 255 / 100) + return self.setLed(begin, end, red, green, blue) + + def get_input(self, addr): + if addr >= ADDR_AI_FIRST and addr <= ADDR_AI_LAST: + return self.analogRead(addr) + elif addr >= ADDR_DI_FIRST and addr <= ADDR_DI_LAST: + return self.digitalRead(addr) + + def set_output(self, addr, value): + if addr >= ADDR_DO_FIRST and addr <= ADDR_DO_LAST: + self.digitalWrite(addr, value) diff --git a/audio.py b/audio.py index 1d7798c1..6841d651 100644 --- a/audio.py +++ b/audio.py @@ -35,7 +35,7 @@ # [END import_libraries] # Audio recording parameters -RATE = 16000 +RATE = 44100 CHUNK = int(RATE / 10) # 100ms FORMAT = pyaudio.paInt16 diff --git a/audioControls.py b/audioControls.py new file mode 100644 index 00000000..be87d899 --- /dev/null +++ b/audioControls.py @@ -0,0 +1,52 @@ +# CoderBot, a didactical programmable robot. +# Copyright (C) 2014, 2015 Roberto Previtera +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +############################################################################ +# CoderBot, a didactical programmable robot. +# Copyright (C) 2014, 2015 Roberto Previtera +# +# MUSICAL EXTENTION for CoderBot +# This extention is develop by: +# Michele Carbonera - miki_992@hotmail.it - m.carbonera@campus.unimib.it - michele.carbonera@unimib.it +# Antonino Tramontana - a.tramontana1@campus.unimib.it +# Copyright (C) 2020 +############################################################################ + +import os +import alsaaudio + +class AudioCtrl: + mixer = None + _instance = None + + @classmethod + def get_instance(cls): + if cls._instance is None: + cls._instance = AudioCtrl() + return cls._instance + + def __init__(self): + self.mixer = alsaaudio.Mixer('Headphone') + + def getVolume(self): + print(self.mixer.getvolume()) + + def setVolume(self,valueVolume): + self.mixer.setvolume(valueVolume) + +# if __name__ == "__main__": +# a = AudioCtrl() +# a.setVolume(20) diff --git a/cnn_classifier.py b/cnn_classifier.py index 0b80453f..d8e61ce8 100644 --- a/cnn_classifier.py +++ b/cnn_classifier.py @@ -23,7 +23,15 @@ import logging import numpy as np -from tensorflow.lite.python.interpreter import Interpreter +try: + from tensorflow.lite.python.interpreter import Interpreter +except: + logging.warning("tensorflow not available (for inference)") +try: + from tflite_runtime.interpreter import Interpreter +except: + logging.warning("tflite not available") + import cv2 logger = logging.getLogger(__name__) @@ -31,8 +39,7 @@ class CNNClassifier(object): def __init__(self, model_file, label_file): logger.info(model_file) - self._interpreter = Interpreter(model_path=model_file) - self._interpreter.set_num_threads(4) + self._interpreter = Interpreter(model_path=model_file, num_threads=4) self._interpreter.allocate_tensors() self._labels = self.load_labels(label_file) self._input_details = self._interpreter.get_input_details() diff --git a/cnn_manager.py b/cnn_manager.py index f8bfc7dd..0294c9b1 100644 --- a/cnn_manager.py +++ b/cnn_manager.py @@ -26,7 +26,11 @@ import json import threading -from cnn_train import CNNTrainer +try: + from cnn_train import CNNTrainer +except: + logging.warning("tensorflow not available (for training)") + from cnn_classifier import CNNClassifier MODEL_PATH = "./cnn_models" @@ -117,6 +121,7 @@ def load_model(self, model_name): return CNNClassifier(model_file=MODEL_PATH + "/" + model_name + ".tflite", label_file=MODEL_PATH + "/" + model_name + ".txt") return None + class TrainThread(threading.Thread): def __init__(self, manager, model_name, architecture, image_tags, photos_metadata, training_steps, learning_rate): diff --git a/coderbot-copy.py b/coderbot-copy.py deleted file mode 100644 index b7295dfa..00000000 --- a/coderbot-copy.py +++ /dev/null @@ -1,431 +0,0 @@ -############################################################################ -# CoderBot, a didactical programmable robot. -# Copyright (C) 2014, 2015 Roberto Previtera -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -############################################################################ - -import os -import time -import threading -import logging -import pigpio -import sonar -import mpu - -PIN_MOTOR_ENABLE = 22 -PIN_LEFT_FORWARD = 25 -PIN_LEFT_BACKWARD = 24 -PIN_RIGHT_FORWARD = 4 -PIN_RIGHT_BACKWARD = 17 -PIN_PUSHBUTTON = 11 -PIN_SERVO_3 = 9 -PIN_SERVO_4 = 10 -PIN_SONAR_1_TRIGGER = 18 -PIN_SONAR_1_ECHO = 7 -PIN_SONAR_2_TRIGGER = 18 -PIN_SONAR_2_ECHO = 8 -PIN_SONAR_3_TRIGGER = 18 -PIN_SONAR_3_ECHO = 23 -PIN_ENCODER_LEFT = 15 -PIN_ENCODER_RIGHT = 14 - -PWM_FREQUENCY = 100 #Hz -PWM_RANGE = 100 #0-100 - -class CoderBot(object): - - # pylint: disable=too-many-instance-attributes - - _pin_out = [PIN_MOTOR_ENABLE, PIN_LEFT_FORWARD, PIN_RIGHT_FORWARD, PIN_LEFT_BACKWARD, PIN_RIGHT_BACKWARD, PIN_SERVO_3, PIN_SERVO_4] - - def __init__(self, servo=False, motor_trim_factor=1.0, encoder=False): - self.pi = pigpio.pi('localhost') - self.pi.set_mode(PIN_PUSHBUTTON, pigpio.INPUT) - self._cb = dict() - self._cb_last_tick = dict() - self._cb_elapse = dict() - self._servo = servo - self._encoder = encoder - self._motor_trim_factor = motor_trim_factor - if self._servo: - self.motor_control = self._servo_motor - elif self._encoder: - self._twin_motors_enc = self.TwinMotorsEncoder( - self.pi, - pin_enable=PIN_MOTOR_ENABLE, - pin_forward_left=PIN_LEFT_FORWARD, - pin_backward_left=PIN_LEFT_BACKWARD, - pin_encoder_left=PIN_ENCODER_LEFT, - pin_forward_right=PIN_RIGHT_FORWARD, - pin_backward_right=PIN_RIGHT_BACKWARD, - pin_encoder_right=PIN_ENCODER_RIGHT) - self.motor_control = self._dc_enc_motor - else: - self.motor_control = self._dc_motor - - self._cb1 = self.pi.callback(PIN_PUSHBUTTON, pigpio.EITHER_EDGE, self._cb_button) - - for pin in self._pin_out: - self.pi.set_PWM_frequency(pin, PWM_FREQUENCY) - self.pi.set_PWM_range(pin, PWM_RANGE) - - self.sonar = [sonar.Sonar(self.pi, PIN_SONAR_1_TRIGGER, PIN_SONAR_1_ECHO), - sonar.Sonar(self.pi, PIN_SONAR_2_TRIGGER, PIN_SONAR_2_ECHO), - sonar.Sonar(self.pi, PIN_SONAR_3_TRIGGER, PIN_SONAR_3_ECHO)] - - try: - self._ag = mpu.AccelGyro() - except IOError: - logging.info("MPU not available") - - self.stop() - self._is_moving = False - - the_bot = None - - def exit(self): - self._cb1.cancel() - if self._encoder: - self._twin_motors_enc.exit() - for s in self.sonar: - s.cancel() - - @classmethod - def get_instance(cls, servo=False, motor_trim_factor=1.0): - if not cls.the_bot: - cls.the_bot = CoderBot(servo, motor_trim_factor) - return cls.the_bot - - def move(self, speed=100, elapse=-1, steps=-1): - speed_left = min(100, max(-100, speed * self._motor_trim_factor)) - speed_right = min(100, max(-100, speed / self._motor_trim_factor)) - self.motor_control(speed_left=speed_left, speed_right=speed_right, elapse=elapse, steps_left=steps, steps_right=steps) - - def turn(self, speed=100, elapse=-1, steps=-1): - speed_left = min(100, max(-100, speed * self._motor_trim_factor)) - speed_right = -min(100, max(-100, speed / self._motor_trim_factor)) - self.motor_control(speed_left=speed_left, speed_right=speed_right, elapse=elapse, steps_left=steps, steps_right=steps) - - def turn_angle(self, speed=100, angle=0): - z = self._ag.get_gyro_data()['z'] - self.turn(speed, elapse=-1) - while abs(z - self._ag.get_gyro_data()['z']) < angle: - time.sleep(0.05) - logging.info(self._ag.get_gyro_data()['z']) - self.stop() - - def forward(self, speed=100, elapse=-1): - self.move(speed=speed, elapse=elapse) - - def backward(self, speed=100, elapse=-1): - self.move(speed=-speed, elapse=elapse) - - def left(self, speed=100, elapse=-1): - self.turn(speed=-speed, elapse=elapse) - - def right(self, speed=100, elapse=-1): - self.turn(speed=speed, elapse=elapse) - - def servo3(self, angle): - self._servo_control(PIN_SERVO_3, angle) - - def servo4(self, angle): - self._servo_control(PIN_SERVO_4, angle) - - def get_sonar_distance(self, sonar_id=0): - return self.sonar[sonar_id].get_distance() - - def _dc_enc_motor(self, speed_left=100, speed_right=100, elapse=-1, steps_left=-1, steps_right=-1): - self._twin_motors_enc.control(power_left=speed_left, power_right=speed_right, - elapse=elapse, speed_left=speed_left, speed_right=speed_right, - steps_left=steps_left, steps_right=steps_right) - - def _dc_motor(self, speed_left=100, speed_right=100, elapse=-1, steps_left=-1, steps_right=-1): - - # pylint: disable=too-many-instance-attributes - - self._encoder_cur_left = 0 - self._encoder_cur_right = 0 - self._encoder_target_left = steps_left - self._encoder_target_right = steps_right - self._encoder_dir_left = (speed_left > 0) - (speed_left < 0) - self._encoder_dir_right = (speed_right > 0) - (speed_right < 0) - self._encoder_last_tick_time_left = 0 - self._encoder_last_tick_time_right = 0 - self._encoder_motor_stopping_left = False - self._encoder_motor_stopping_right = False - self._encoder_motor_stopped_left = False - self._encoder_motor_stopped_right = False - - self._is_moving = True - if speed_left < 0: - speed_left = abs(speed_left) - self.pi.write(PIN_LEFT_FORWARD, 0) - self.pi.set_PWM_dutycycle(PIN_LEFT_BACKWARD, speed_left) - else: - self.pi.write(PIN_LEFT_BACKWARD, 0) - self.pi.set_PWM_dutycycle(PIN_LEFT_FORWARD, speed_left) - - if speed_right < 0: - speed_right = abs(speed_right) - self.pi.write(PIN_RIGHT_FORWARD, 0) - self.pi.set_PWM_dutycycle(PIN_RIGHT_BACKWARD, speed_right) - else: - self.pi.write(PIN_RIGHT_BACKWARD, 0) - self.pi.set_PWM_dutycycle(PIN_RIGHT_FORWARD, speed_right) - - self.pi.write(PIN_MOTOR_ENABLE, 1) - if elapse > 0: - time.sleep(elapse) - self.stop() - - def _servo_motor(self, speed_left=100, speed_right=100, elapse=-1, steps_left=-1, steps_right=-1): - self._is_moving = True - speed_left = -speed_left - - steps_left - steps_right - - self.pi.write(PIN_MOTOR_ENABLE, 1) - self.pi.write(PIN_RIGHT_BACKWARD, 0) - self.pi.write(PIN_LEFT_BACKWARD, 0) - - self._servo_motor_control(PIN_LEFT_FORWARD, speed_left) - self._servo_motor_control(PIN_RIGHT_FORWARD, speed_right) - if elapse > 0: - time.sleep(elapse) - self.stop() - - - def _servo_motor_control(self, pin, speed): - self._is_moving = True - speed = ((speed + 100) * 50 / 200) + 52 - - self.pi.set_PWM_range(pin, 1000) - self.pi.set_PWM_frequency(pin, 50) - self.pi.set_PWM_dutycycle(pin, speed) - - def _servo_control(self, pin, angle): - duty = ((angle + 90) * 100 / 180) + 25 - - self.pi.set_PWM_range(pin, 1000) - self.pi.set_PWM_frequency(pin, 50) - self.pi.set_PWM_dutycycle(pin, duty) - - def stop(self): - if self._encoder: - self._twin_motors_enc.stop() - else: - for pin in self._pin_out: - self.pi.write(pin, 0) - self._is_moving = False - - def is_moving(self): - return self._is_moving - - def set_callback(self, gpio, callback, elapse): - self._cb_elapse[gpio] = elapse * 1000 - self._cb[gpio] = callback - self._cb_last_tick[gpio] = 0 - - def sleep(self, elapse): - logging.debug("sleep: %s", str(elapse)) - time.sleep(elapse) - - def _cb_button(self, gpio, level, tick): - cb = self._cb.get(gpio) - if cb: - elapse = self._cb_elapse.get(gpio) - if level == 0: - self._cb_last_tick[gpio] = tick - elif tick - self._cb_last_tick[gpio] > elapse: - self._cb_last_tick[gpio] = tick - logging.info("pushed: %d, %d", level, tick) - cb() - - class MotorEncoder(object): - def __init__(self, parent, _pigpio, pin_enable, pin_forward, pin_backward, pin_encoder): - self._parent = parent - self._pigpio = _pigpio - self._pin_enable = pin_enable - self._pin_forward = pin_forward - self._pin_backward = pin_backward - self._pin_encoder = pin_encoder - self._direction = False - self._pin_duty = 0 - self._pin_reverse = 0 - self._power = 0.0 - self._power_actual = 0.0 - self._encoder_dist = 0 - self._encoder_speed = 0.0 - self._encoder_last_tick = 0 - self._encoder_dist_target = 0 - self._encoder_speed_target = 0.0 - self._encoder_k_s_1 = 20 - self._encoder_k_v_1 = 80 - self._motor_stopping = False - self._motor_running = False - self._motor_stop_fast = True - self._pigpio.set_mode(self._pin_encoder, pigpio.INPUT) - self._cb = self._pigpio.callback(self._pin_encoder, pigpio.RISING_EDGE, self._cb_encoder) - self._motor_lock = threading.RLock() - - def exit(self): - self._cb.cancel() - - def _cb_encoder(self, gpio, level, tick): - self._motor_lock.acquire() - self._encoder_dist += 1 - delta_ticks = tick - self._encoder_last_tick if tick > self._encoder_last_tick else tick - self._encoder_last_tick + 4294967295 - self._encoder_last_tick = tick - self._encoder_speed = 1000000.0 / delta_ticks #convert speed in steps per second - #print "pin: " + str(self._pin_forward) + " dist: " + str(self._encoder_dist) + " target: " + str(self._encoder_dist_target) - if self._encoder_dist_target >= 0 and self._motor_stop_fast: - #delta_s is the delta (in steps)before the target to reverse the motor in order to arrive at target - delta_s = max(min(self._encoder_speed / self._encoder_k_s_1, 100), 0) - #print "pin: " + str(self._pin_forward) + " dist: " + str(self._encoder_dist) + " target: " + str(self._encoder_dist_target) + " delta_s: " + str(delta_s) - if (self._encoder_dist >= self._encoder_dist_target - delta_s and - not self._motor_stopping and self._motor_running): - self._motor_stopping = True - self._pigpio.write(self._pin_duty, 0) - self._pigpio.set_PWM_dutycycle(self._pin_reverse, self._power) - elif (self._motor_running and - ((self._motor_stopping and - self._encoder_speed < self._encoder_k_v_1) or - (self._motor_stopping and - self._encoder_dist >= self._encoder_dist_target))): - self.stop() - logging.info("dist: " + str(self._encoder_dist) + " speed: " + str(self._encoder_speed)) - if self._encoder_dist_target >= 0 and not self._motor_stop_fast: - if self._encoder_dist >= self._encoder_dist_target: - self.stop() - self._parent._cb_encoder(self, gpio, level, tick) - self._motor_lock.release() - if not self._motor_running: - self._parent._check_complete() - - def control(self, power=100.0, elapse=-1, speed=100.0, steps=-1): - self._motor_lock.acquire() - self._direction = speed > 0 - self._encoder_dist_target = steps - self._motor_stopping = False - self._motor_running = True - self._encoder_dist = 0 - self._encoder_speed_target = abs(speed) - self._power = abs(power) #TODO: initial power must be a function of desired speed - self._power_actual = abs(power) #TODO: initial power must be a function of desired speed - self._pin_duty = self._pin_forward if self._direction else self._pin_backward - self._pin_reverse = self._pin_backward if self._direction else self._pin_forward - self._pigpio.write(self._pin_reverse, 0) - self._pigpio.set_PWM_dutycycle(self._pin_duty, self._power) - self._pigpio.write(self._pin_enable, True) - self._motor_lock.release() - if elapse > 0: - time.sleep(elapse) - self.stop() - - def stop(self): - self._motor_lock.acquire() - self._motor_stopping = False - self._motor_running = False - self._pigpio.write(self._pin_forward, 0) - self._pigpio.write(self._pin_backward, 0) - self._motor_lock.release() - - def distance(self): - return self._encoder_dist - - def speed(self): - return self._encoder_speed - - def stopping(self): - return self._motor_stopping - - def running(self): - return self._motor_running - - def adjust_power(self, power_delta): - self._power_actual = min(max(self._power + power_delta, 0), 100) - self._pigpio.set_PWM_dutycycle(self._pin_duty, self._power_actual) - - class TwinMotorsEncoder(object): - def __init__(self, apigpio, pin_enable, pin_forward_left, pin_backward_left, pin_encoder_left, pin_forward_right, pin_backward_right, pin_encoder_right): - self._straight = False - self._running = False - self._encoder_sem = threading.Condition() - self._motor_left = CoderBot.MotorEncoder(self, apigpio, pin_enable, pin_forward_left, pin_backward_left, pin_encoder_left) - self._motor_right = CoderBot.MotorEncoder(self, apigpio, pin_enable, pin_forward_right, pin_backward_right, pin_encoder_right) - - def exit(self): - self._motor_left.exit() - self._motor_right.exit() - - def control(self, power_left=100.0, power_right=100.0, elapse=-1, speed_left=-1, speed_right=-1, steps_left=-1, steps_right=-1): - self._straight = power_left == power_right and speed_left == speed_right and steps_left == steps_right - - if steps_left >= 0 or steps_right >= 0: - self._encoder_sem.acquire() - - self._motor_left.control(power=power_left, elapse=-1, speed=speed_left, steps=steps_left) - self._motor_right.control(power=power_right, elapse=-1, speed=speed_right, steps=steps_right) - self._running = True - - if elapse > 0: - time.sleep(elapse) - self.stop() - - if steps_left >= 0 or steps_right >= 0: - self._encoder_sem.wait() - self._encoder_sem.release() - self.stop() - - def stop(self): - self._motor_left.stop() - self._motor_right.stop() - self._running = False - - def distance(self): - return (self._motor_left.distance() + self._motor_right.distance()) / 2 - - def speed(self): - return (self._motor_left.speed() + self._motor_right.speed()) / 2 - - def _cb_encoder(self, motor, gpio, level, tick): - if (self._straight and self._running and not self._motor_left.stopping() and not self._motor_right.stopping() and - abs(self._motor_left.distance() - self._motor_right.distance()) > 2): - distance_delta = self._motor_left.distance() - self._motor_right.distance() - speed_delta = self._motor_left.speed() - self._motor_right.speed() - power_delta = (distance_delta / 2.0) + (speed_delta / 10.0) - #print "power_delta: " + str(power_delta) + " distance_delta: " + str(distance_delta) + " speed_delta: " + str(speed_delta) - if self._motor_left == motor: - self._motor_left.adjust_power(-power_delta) - if self._motor_right == motor: - self._motor_right.adjust_power(power_delta) - - def _check_complete(self): - if self._motor_left.running() is False and self._motor_right.running() is False: - self._encoder_sem.acquire() - self._encoder_sem.notify() - self._encoder_sem.release() - - def halt(self): - os.system('sudo halt') - - def restart(self): - os.system('sudo /etc/init.d/coderbot restart') - - def reboot(self): - os.system('sudo reboot') diff --git a/coderbot.cfg b/coderbot.cfg index 5e8264f9..5f44f821 100644 --- a/coderbot.cfg +++ b/coderbot.cfg @@ -1 +1 @@ -{"move_power_angle_3": "60", "cnn_default_model": "generic_fast_low", "prog_maxblocks": "-1", "camera_jpeg_quality": "5", "show_page_control": "true", "camera_framerate": "30", "prog_scrollbars": "true", "move_fw_speed": "100", "prog_level": "adv", "move_motor_trim": "1", "move_motor_mode": "dc", "cv_image_factor": "2", "move_power_angle_1": "45", "camera_path_object_size_min": "4000", "button_func": "none", "camera_color_object_size_min": "4000", "camera_jpeg_bitrate": "1000000", "move_fw_elapse": "1", "show_control_move_commands": "true", "camera_color_object_size_max": "160000", "show_page_prefs": "true", "camera_exposure_mode": "auto", "ctrl_tr_elapse": "-1", "show_page_program": "true", "move_tr_elapse": "0.5", "camera_path_object_size_max": "160000", "sound_shutter": "$shutter.mp3", "ctrl_fw_elapse": "-1", "sound_stop": "$shutdown.mp3", "ctrl_tr_speed": "80", "ctrl_fw_speed": "100", "move_tr_speed": "85", "move_power_angle_2": "60", "ctrl_hud_image": "", "load_at_start": "", "sound_start": "$startup.mp3", "encoder": "True", "wifi_mode": "ap", "wifi_ssid": "", "wifi_psk": ""} \ No newline at end of file +{"move_power_angle_3": "60", "cnn_default_model": "generic_fast_low", "prog_maxblocks": "-1", "camera_jpeg_quality": "5", "show_page_control": "true", "camera_framerate": "30", "prog_scrollbars": "true", "move_fw_speed": "100", "prog_level": "adv", "move_motor_trim": "1", "move_motor_mode": "dc", "cv_image_factor": "2", "move_power_angle_1": "45", "camera_path_object_size_min": "4000", "button_func": "none", "camera_color_object_size_min": "4000", "camera_jpeg_bitrate": "1000000", "move_fw_elapse": "1", "show_control_move_commands": "true", "camera_color_object_size_max": "160000", "show_page_prefs": "true", "camera_exposure_mode": "auto", "ctrl_tr_elapse": "-1", "show_page_program": "true", "move_tr_elapse": "0.5", "camera_path_object_size_max": "160000", "sound_shutter": "$shutter.wav", "ctrl_fw_elapse": "-1", "sound_stop": "$shutdown.wav", "ctrl_tr_speed": "80", "ctrl_fw_speed": "100", "move_tr_speed": "85", "move_power_angle_2": "60", "ctrl_hud_image": "", "load_at_start": "", "sound_start": "$startup.wav", "encoder": "True", "audio_volume_level": "100", "wifi_mode": "ap", "wifi_ssid": "coderbot", "wifi_psk": "coderbot", "packages_installed": ""} diff --git a/coderbot.py b/coderbot.py index 93f2e6a6..1c05db79 100644 --- a/coderbot.py +++ b/coderbot.py @@ -46,7 +46,7 @@ class GPIO_CODERBOT_V_4(): PIN_SONAR_3_TRIGGER = 18 PIN_SONAR_3_ECHO = 23 PIN_SONAR_4_TRIGGER = 18 - PIN_SONAR_4_ECHO = None + PIN_SONAR_4_ECHO = 13 # encoder PIN_ENCODER_LEFT_A = 14 @@ -93,12 +93,11 @@ class CoderBot(object): def __init__(self, motor_trim_factor=1.0, encoder=True): try: self._mpu = mpu.AccelGyroMag() - self.GPIOS = GPIO_CODERBOT_V_5() logging.info("MPU available") except: logging.info("MPU not available") - self.GPIOS = GPIO_CODERBOT_V_4() + self.GPIOS = GPIO_CODERBOT_V_5() self._pin_out = [self.GPIOS.PIN_LEFT_FORWARD, self.GPIOS.PIN_RIGHT_FORWARD, self.GPIOS.PIN_LEFT_BACKWARD, self.GPIOS.PIN_RIGHT_BACKWARD, self.GPIOS.PIN_SERVO_1, self.GPIOS.PIN_SERVO_2] self.pi = pigpio.pi('localhost') self.pi.set_mode(self.GPIOS.PIN_PUSHBUTTON, pigpio.INPUT) diff --git a/cv/camera.py b/cv/camera.py index 570d0a37..f2b1b68f 100644 --- a/cv/camera.py +++ b/cv/camera.py @@ -29,7 +29,7 @@ class Camera(object): - FFMPEG_CMD = 'MP4Box' + FFMPEG_CMD = 'ffmpeg' PHOTO_FILE_EXT = ".jpg" VIDEO_FILE_EXT = ".mp4" VIDEO_FILE_EXT_H264 = '.h264' @@ -90,7 +90,10 @@ def video_stop(self): self.camera.stop_recording(2) # pack in mp4 container - params = " -fps " + str(self.camera.framerate) + " -add " + self.video_filename + self.VIDEO_FILE_EXT_H264 + " " + self.video_filename + self.VIDEO_FILE_EXT + params = " -loglevel quiet -stats -framerate " + str(self.camera.framerate) + \ + " -i " + self.video_filename + self.VIDEO_FILE_EXT_H264 + \ + " -c copy " + self.video_filename + self.VIDEO_FILE_EXT + os.system(self.FFMPEG_CMD + params) # remove h264 file os.remove(self.video_filename + self.VIDEO_FILE_EXT_H264) diff --git a/data/activities.json b/data/activities.json index e9b18176..674b1c99 100644 --- a/data/activities.json +++ b/data/activities.json @@ -1 +1 @@ -{"_default": {}} \ No newline at end of file +{"_default": {"2": {"stock": true, "default": true, "uiLang": null, "defaultView": null, "exec": {"camera": true, "log": true}, "name": "default", "drawerEnabled": true, "showName": true, "buttons": [{"action": "clearProgramDlg", "icon": "clear", "label": "message.activity_program_clear", "type": "text"}, {"action": "saveProgram", "icon": "save", "label": "message.activity_program_save", "type": "text"}, {"action": "toggleSaveAs", "icon": "edit", "label": "message.activity_program_save_as", "type": "text"}, {"action": "loadProgramList", "icon": "folder_open", "label": "message.activity_program_load", "type": "text"}, {"action": "runProgram", "icon": "play_arrow", "label": "message.activity_program_run", "type": "text"}, {"action": "getProgramCode", "icon": "code", "label": "message.activity_program_show_code", "type": "text"}, {"action": "exportProgram", "icon": "fa-file-export", "label": "message.activity_program_export", "type": "text"}, {"action": "pickFile", "icon": "fa-file-import", "label": "message.activity_program_import", "type": "text"}], "description": "", "fontSize": "Medio", "capsSwitch": true, "bodyFont": "Roboto", "codeFont": "ubuntumono", "maxBlocks": 0, "availableViews": [], "viewSource": null, "autoRecVideo": null, "toolbox": {"contents": [{"colour": "210", "contents": [{"kind": "block", "type": "controls_if"}, {"kind": "block", "type": "logic_compare"}, {"kind": "block", "type": "logic_operation"}, {"kind": "block", "type": "logic_negate"}, {"kind": "block", "type": "logic_boolean"}, {"kind": "block", "type": "logic_null"}, {"kind": "block", "type": "logic_ternary"}], "kind": "category", "name": "Logica"}, {"colour": "120", "contents": [{"kind": "block", "type": "controls_repeat_ext", "value": {"contents": {"field": {"#text": "10", "name": "NUM"}, "kind": "block", "type": "math_number"}, "name": "TIMES"}}, {"kind": "block", "type": "controls_whileUntil"}, {"kind": "block", "type": "controls_for", "value": [{"contents": {"field": {"#text": "1", "name": "NUM"}, "kind": "block", "type": "math_number"}, "name": "FROM"}, {"contents": {"field": {"#text": "10", "name": "NUM"}, "kind": "block", "type": "math_number"}, "name": "TO"}, {"contents": {"field": {"#text": "1", "name": "NUM"}, "kind": "block", "type": "math_number"}, "name": "BY"}]}, {"kind": "block", "type": "controls_forEach"}, {"kind": "block", "type": "controls_flow_statements"}], "kind": "category", "name": "Cicli"}, {"colour": "230", "contents": [{"kind": "block", "type": "math_number"}, {"kind": "block", "type": "math_arithmetic"}, {"kind": "block", "type": "math_single"}, {"kind": "block", "type": "math_trig"}, {"kind": "block", "type": "math_constant"}, {"kind": "block", "type": "math_number_property"}, {"kind": "block", "type": "math_change"}, {"kind": "block", "type": "math_round"}, {"kind": "block", "type": "math_on_list"}, {"kind": "block", "type": "math_modulo"}, {"kind": "block", "type": "math_constrain"}, {"kind": "block", "type": "math_random_int"}, {"kind": "block", "type": "math_random_float"}], "kind": "category", "name": "Matematica"}, {"colour": "160", "contents": [{"kind": "block", "type": "text_print"}, {"kind": "block", "type": "text"}, {"kind": "block", "type": "text_join"}, {"kind": "block", "type": "text_append"}, {"kind": "block", "type": "text_length"}, {"kind": "block", "type": "text_isEmpty"}, {"kind": "block", "type": "text_indexOf"}, {"kind": "block", "type": "text_charAt"}, {"kind": "block", "type": "text_getSubstring"}, {"kind": "block", "type": "text_changeCase"}, {"kind": "block", "type": "text_trim"}], "kind": "category", "name": "Testo"}, {"colour": "260", "contents": [{"kind": "block", "type": "lists_create_empty"}, {"kind": "block", "type": "lists_create_with"}, {"kind": "block", "type": "lists_repeat"}, {"kind": "block", "type": "lists_length"}, {"kind": "block", "type": "lists_isEmpty"}, {"kind": "block", "type": "lists_indexOf"}, {"kind": "block", "type": "lists_getIndex"}, {"kind": "block", "type": "lists_setIndex"}, {"kind": "block", "type": "lists_getSublist"}, {"kind": "block", "type": "hashmap_get_value"}, {"kind": "block", "type": "hashmap_get_keys"}], "kind": "category", "name": "Liste"}, {"colour": "330", "custom": "VARIABLE", "kind": "category", "name": "Variabili"}, {"colour": "290", "custom": "PROCEDURE", "kind": "category", "name": "Funzioni"}, {"colour": "15", "contents": [{"kind": "block", "type": "coderbot_event_generator", "value": {"contents": {"kind": "block", "type": "controls_if", "value": [{"contents": {"type": "logic_compare"}, "name": "IF0"}, {"contents": {"field": {"#text": "a_topic", "name": "event_topic"}, "kind": "block", "type": "coderbot_event_publisher", "value": {"contents": {"#text": "\n\t\t\t\t\t\t\t\t", "kind": "block", "type": "text"}, "name": "event_data"}}, "name": "DO0"}]}, "name": "generator_statements"}}, {"field": {"#text": "a_topic", "name": "event_topic"}, "kind": "block", "type": "coderbot_event_publisher", "value": {"contents": {"#text": "\n\t\t\t\t", "kind": "block", "type": "text"}, "name": "event_data"}}, {"field": {"#text": "a_topic", "name": "event_topic"}, "kind": "block", "type": "coderbot_event_listener", "value": {"contents": {"kind": "block", "type": "text_print", "value": {"contents": {"field": {"#text": "event_data", "name": "VAR"}, "kind": "block", "type": "variables_get"}, "name": "TEXT"}}, "name": "event_statements"}}, {"field": {"#text": "event_data", "name": "VAR"}, "kind": "block", "type": "variables_get"}], "kind": "category", "name": "Eventi"}, {"colour": "40", "contents": [{"kind": "block", "type": "coderbot_moveForward"}, {"kind": "block", "type": "coderbot_moveBackward"}, {"kind": "block", "type": "coderbot_turnLeft"}, {"kind": "block", "type": "coderbot_turnRight"}, {"kind": "block", "type": "coderbot_adv_stop"}], "kind": "category", "name": "Movimento"}, {"colour": "120", "contents": [{"kind": "block", "type": "coderbot_camera_photoTake"}, {"kind": "block", "type": "coderbot_camera_videoRec"}, {"kind": "block", "type": "coderbot_camera_videoStop"}], "kind": "category", "name": "Camera"}, {"colour": "250", "contents": [{"kind": "block", "type": "coderbot_cam_average"}, {"kind": "block", "type": "coderbot_adv_pathAhead"}, {"kind": "block", "type": "coderbot_adv_findLine"}, {"kind": "block", "type": "coderbot_adv_findSignal"}, {"kind": "block", "type": "coderbot_adv_findFace"}, {"kind": "block", "type": "coderbot_adv_findColor", "value": {"contents": {"#text": "\n\t\t\t\t", "kind": "block", "type": "colour_picker"}, "name": "COLOR"}}, {"kind": "block", "type": "coderbot_adv_findText", "value": {"contents": {"#text": "\n\t\t\t\t", "kind": "block", "type": "colour_picker"}, "name": "COLOR"}}, {"kind": "block", "type": "coderbot_adv_findQRCode"}, {"kind": "block", "type": "coderbot_adv_findARCode"}, {"kind": "block", "type": "coderbot_adv_cnn_classify"}, {"kind": "block", "type": "coderbot_adv_cnn_detect_objects"}], "kind": "category", "name": "Visione"}, {"colour": "240", "contents": [{"kind": "block", "type": "coderbot_sonar_get_distance"}, {"kind": "block", "type": "coderbot_mpu_get_gyro"}, {"kind": "block", "type": "coderbot_mpu_get_accel"}, {"kind": "block", "type": "coderbot_mpu_get_heading"}, {"kind": "block", "type": "coderbot_mpu_get_temp"}], "kind": "category", "name": "Sensori"}, {"colour": "220", "contents": [{"kind": "block", "type": "coderbot_audio_say"}, {"kind": "block", "type": "coderbot_audio_record"}, {"kind": "block", "type": "coderbot_audio_play"}, {"kind": "block", "type": "coderbot_audio_hear", "value": [{"contents": {"field": {"#text": "100", "name": "NUM"}, "kind": "block", "type": "math_number"}, "name": "LEVEL"}, {"contents": {"field": {"#text": "1.0", "name": "NUM"}, "kind": "block", "type": "math_number"}, "name": "ELAPSE"}]}, {"kind": "block", "type": "coderbot_audio_listen"}], "kind": "category", "name": "Suoni"}, {"colour": "240", "contents": [{"kind": "block", "type": "coderbot_atmega_get_input"}, {"kind": "block", "type": "coderbot_atmega_set_output"}, {"kind": "block", "type": "coderbot_atmega_set_led"}], "kind": "category", "name": "Estensioni I/O"}, {"colour": "345", "contents": [{"kind": "block", "type": "coderbot_music_note_adv"}, {"kind": "block", "type": "coderbot_music_instrument_adv"}, {"kind": "block", "type": "coderbot_music_animal_adv"}, {"kind": "block", "type": "coderbot_music_pause_adv"}], "kind": "category", "name": "Music"}], "kind": "categoryToolbox"}}, "3": {"stock": null, "default": null, "uiLang": null, "defaultView": "blocks", "exec": {"camera": true, "log": true}, "name": "Base", "drawerEnabled": true, "showName": true, "buttons": [{"action": "clearProgramDlg", "icon": "clear", "label": "message.activity_program_clear", "type": "text"}, {"action": "saveProgram", "icon": "save", "label": "message.activity_program_save", "type": "text"}, {"action": "toggleSaveAs", "icon": "edit", "label": "message.activity_program_save_as", "type": "text"}, {"action": "loadProgramList", "icon": "folder_open", "label": "message.activity_program_load", "type": "text"}, {"action": "runProgram", "icon": "play_arrow", "label": "message.activity_program_run", "type": "text"}, {"action": "getProgramCode", "icon": "code", "label": "message.activity_program_show_code", "type": "text"}, {"action": "exportProgram", "icon": "fa-file-export", "label": "message.activity_program_export", "type": "text"}, {"action": "pickFile", "icon": "fa-file-import", "label": "message.activity_program_import", "type": "text"}], "description": "Base", "fontSize": "Medio", "capsSwitch": true, "bodyFont": "Roboto", "codeFont": "ubuntumono", "maxBlocks": 0, "availableViews": [], "viewSource": null, "autoRecVideo": null, "toolbox": {"kind": "flyoutToolbox", "contents": [{"kind": "block", "type": "coderbot_basic_moveForward", "enabled": true}, {"kind": "block", "type": "coderbot_basic_moveBackward", "enabled": true}, {"kind": "block", "type": "coderbot_basic_turnLeft", "enabled": true}, {"kind": "block", "type": "coderbot_basic_turnRight", "enabled": true}, {"kind": "block", "type": "coderbot_basic_repeat", "enabled": true}, {"kind": "block", "type": "coderbot_basic_audio_say", "enabled": true}, {"kind": "block", "type": "coderbot_basic_camera_photoTake", "enabled": true}]}}}} \ No newline at end of file diff --git a/data/defaults/programs/program_demo_cat_follower.json b/data/defaults/programs/program_demo_cat_follower.json new file mode 100644 index 00000000..376d1fb8 --- /dev/null +++ b/data/defaults/programs/program_demo_cat_follower.json @@ -0,0 +1 @@ +{"name": "cat_follower", "dom_code": "objectclasspositionpos_xWHILETRUEobjectGETFIRSTgeneric_object_detectclassGETFIRSTobjectEQclasscatpositionGETFROM_STARTobject3pos_xDIVIDEADDGETFROM_STARTposition1GETFROM_STARTposition32classLTpos_x40LEFT600.1GTpos_x60RIGHT600.1FORWARD1000.2object", "code": "object2 = None\nclass2 = None\nposition = None\npos_x = None\n\n\nwhile True:\n get_prog_eng().check_end()\n object2 = get_cam().cnn_detect_objects(\"generic_object_detect\")[0]\n class2 = object2[0]\n if class2 == 'cat':\n position = object2[2]\n pos_x = (position[0] + position[2]) / 2\n get_cam().set_text(class2)\n if pos_x < 40:\n get_bot().left(speed=60, elapse=0.1)\n elif pos_x > 60:\n get_bot().right(speed=60, elapse=0.1)\n else:\n get_bot().forward(speed=100, elapse=0.2)\n else:\n get_cam().set_text(object2)\n", "default": false} \ No newline at end of file diff --git a/data/defaults/programs/program_demo_io_ext.json b/data/defaults/programs/program_demo_io_ext.json new file mode 100644 index 00000000..3d2c518a --- /dev/null +++ b/data/defaults/programs/program_demo_io_ext.json @@ -0,0 +1 @@ +{"name": "test_io_ext", "dom_code": "Analog_Input_1WHILETRUEAnalog_Input_10Analog Input 1: Analog_Input_1GTAnalog_Input_11000TRUE0FALSE", "code": "Analog_Input_1 = None\n\n\nwhile True:\n get_prog_eng().check_end()\n Analog_Input_1 = get_atmega().get_input(0)\n get_cam().set_text('Analog Input 1: ' + str(Analog_Input_1))\n if Analog_Input_1 > 100:\n get_atmega().set_output(0, True)\n else:\n get_atmega().set_output(0, False)\n", "default": false} \ No newline at end of file diff --git a/data/defaults/programs/program_test_input.json b/data/defaults/programs/program_test_input.json new file mode 100644 index 00000000..309b3a4f --- /dev/null +++ b/data/defaults/programs/program_test_input.json @@ -0,0 +1 @@ +{"name": "test_input", "dom_code": "WHILETRUEanalog 1: 0 analog 2: 1 digital 1: 2", "code": "while True:\n get_prog_eng().check_end()\n get_cam().set_text(''.join([str(x) for x in ['analog 1: ', get_atmega().get_input(0), ' analog 2: ', get_atmega().get_input(1), ' digital 1: ', get_atmega().get_input(2)]]))\n", "default": false} \ No newline at end of file diff --git a/data/defaults/programs/program_test_music.json b/data/defaults/programs/program_test_music.json new file mode 100644 index 00000000..9516746c --- /dev/null +++ b/data/defaults/programs/program_test_music.json @@ -0,0 +1 @@ +{"name": "test_music", "dom_code": "C2nonedog1D2nonecat1E2nonepig1F2noneelephant1G2nonesnake1A2noneduck1B2nonecat1", "code": "get_music().play_note(note=\"C2\", alteration=\"none\" ,instrument=\"dog\" ,duration=1)\nget_music().play_note(note=\"D2\", alteration=\"none\" ,instrument=\"cat\" ,duration=1)\nget_music().play_note(note=\"E2\", alteration=\"none\" ,instrument=\"pig\" ,duration=1)\nget_music().play_note(note=\"F2\", alteration=\"none\" ,instrument=\"elephant\" ,duration=1)\nget_music().play_note(note=\"G2\", alteration=\"none\" ,instrument=\"snake\" ,duration=1)\nget_music().play_note(note=\"A2\", alteration=\"none\" ,instrument=\"duck\" ,duration=1)\nget_music().play_note(note=\"B2\", alteration=\"none\" ,instrument=\"cat\" ,duration=1)\n", "default": false} \ No newline at end of file diff --git a/data/defaults/programs/program_test_output.json b/data/defaults/programs/program_test_output.json new file mode 100644 index 00000000..954474ae --- /dev/null +++ b/data/defaults/programs/program_test_output.json @@ -0,0 +1 @@ +{"name": "test_output", "dom_code": "WHILETRUE0TRUE0.11TRUE0.12TRUE0.10FALSE0.11FALSE0.12FALSE0.1", "code": "while True:\n get_prog_eng().check_end()\n get_atmega().set_output(0, True)\n get_bot().sleep(0.1)\n get_atmega().set_output(1, True)\n get_bot().sleep(0.1)\n get_atmega().set_output(2, True)\n get_bot().sleep(0.1)\n get_atmega().set_output(0, False)\n get_bot().sleep(0.1)\n get_atmega().set_output(1, False)\n get_bot().sleep(0.1)\n get_atmega().set_output(2, False)\n get_bot().sleep(0.1)\n", "default": false} \ No newline at end of file diff --git a/data/program_no_name.json b/data/program_no_name.json new file mode 100644 index 00000000..8d987595 --- /dev/null +++ b/data/program_no_name.json @@ -0,0 +1 @@ +{"name": "no_name", "dom_code": "", "code": "", "default": false} \ No newline at end of file diff --git a/data/program_test_led.json b/data/program_test_led.json new file mode 100644 index 00000000..c95f304b --- /dev/null +++ b/data/program_test_led.json @@ -0,0 +1 @@ +{"name": "test_led", "dom_code": "ledsicleds6031leds000i1leds11MINUSi1000ADDi5leds000c11005iADDi5MULTIPLYc0MULTIPLYc0MULTIPLYc31leds000", "code": "leds = None\ni = None\nc = None\n\ndef upRange(start, stop, step):\n while start <= stop:\n yield start\n start += abs(step)\n\ndef downRange(start, stop, step):\n while start >= stop:\n yield start\n start -= abs(step)\n\n\nleds = 60\nfor count in range(3):\n get_prog_eng().check_end()\n get_atmega().set_led(1, leds, 0, 0, 0)\n for i in (1 <= float(leds)) and upRange(1, float(leds), 1) or downRange(1, float(leds), 1):\n get_prog_eng().check_end()\n get_atmega().set_led(1, i - 1, 0, 0, 0)\n get_atmega().set_led(i + 5, leds, 0, 0, 0)\n for c in range(1, 101, 5):\n get_prog_eng().check_end()\n get_atmega().set_led(i, i + 5, c * 0, c * 0, c * 3)\n get_atmega().set_led(1, leds, 0, 0, 0)\n", "default": false} \ No newline at end of file diff --git a/data/programs.json b/data/programs.json index 4e899519..e2968f2b 100644 --- a/data/programs.json +++ b/data/programs.json @@ -1 +1 @@ -{"_default": {"1": {"name": "test_find_code", "filename": "./data/defaults/programs/program_test_find_code.json", "default": "True"}, "2": {"name": "demo_color_seeker", "filename": "./data/defaults/programs/program_demo_color_seeker.json", "default": "True"}, "3": {"name": "demo_sound_clap_control", "filename": "./data/defaults/programs/program_demo_sound_clap_control.json", "default": "True"}, "4": {"name": "test_find_path_ahead", "filename": "./data/defaults/programs/program_test_find_path_ahead.json", "default": "True"}, "5": {"name": "test_sound_hear", "filename": "./data/defaults/programs/program_test_sound_hear.json", "default": "True"}, "6": {"name": "test_find_color", "filename": "./data/defaults/programs/program_test_find_color.json", "default": "True"}, "7": {"name": "test_cnn_classifier", "filename": "./data/defaults/programs/program_test_cnn_classifier.json", "default": "True"}, "8": {"name": "test_sound_rec", "filename": "./data/defaults/programs/program_test_sound_rec.json", "default": "True"}, "9": {"name": "test_find_face", "filename": "./data/defaults/programs/program_test_find_face.json", "default": "True"}, "10": {"name": "demo_obstacle_avoidance", "filename": "./data/defaults/programs/program_demo_obstacle_avoidance.json", "default": "True"}, "11": {"name": "test_sonars", "filename": "./data/defaults/programs/program_test_sonars.json", "default": "True"}, "12": {"name": "test_img_average", "filename": "./data/defaults/programs/program_test_img_average.json", "default": "True"}, "13": {"name": "demo_ar_tags", "filename": "./data/defaults/programs/program_demo_ar_tags.json", "default": "True"}, "14": {"name": "test_cnn_object_detect", "filename": "./data/defaults/programs/program_test_cnn_object_detect.json", "default": "True"}, "15": {"name": "demo_line_follower", "filename": "./data/defaults/programs/program_demo_line_follower.json", "default": "True"}}} \ No newline at end of file +{"_default": {"1": {"name": "test_find_code", "filename": "./data/defaults/programs/program_test_find_code.json", "default": "True"}, "2": {"name": "demo_color_seeker", "filename": "./data/defaults/programs/program_demo_color_seeker.json", "default": "True"}, "3": {"name": "demo_sound_clap_control", "filename": "./data/defaults/programs/program_demo_sound_clap_control.json", "default": "True"}, "4": {"name": "test_find_path_ahead", "filename": "./data/defaults/programs/program_test_find_path_ahead.json", "default": "True"}, "5": {"name": "test_sound_hear", "filename": "./data/defaults/programs/program_test_sound_hear.json", "default": "True"}, "6": {"name": "test_find_color", "filename": "./data/defaults/programs/program_test_find_color.json", "default": "True"}, "7": {"name": "test_cnn_classifier", "filename": "./data/defaults/programs/program_test_cnn_classifier.json", "default": "True"}, "8": {"name": "test_sound_rec", "filename": "./data/defaults/programs/program_test_sound_rec.json", "default": "True"}, "9": {"name": "test_find_face", "filename": "./data/defaults/programs/program_test_find_face.json", "default": "True"}, "10": {"name": "demo_obstacle_avoidance", "filename": "./data/defaults/programs/program_demo_obstacle_avoidance.json", "default": "True"}, "11": {"name": "test_img_average", "filename": "./data/defaults/programs/program_test_img_average.json", "default": "True"}, "12": {"name": "demo_ar_tags", "filename": "./data/defaults/programs/program_demo_ar_tags.json", "default": "True"}, "13": {"name": "test_cnn_object_detect", "filename": "./data/defaults/programs/program_test_cnn_object_detect.json", "default": "True"}, "14": {"name": "demo_line_follower", "filename": "./data/defaults/programs/program_demo_line_follower.json", "default": "True"}, "15": {"name": "cat_follower", "filename": "./data/defaults/programs/program_demo_cat_follower.json", "default": "True"}, "16": {"name": "test_music", "filename": "./data/defaults/programs/program_test_music.json", "default": "True"}, "17": {"name": "demo_cat_follower", "filename": "./data/defaults/programs/program_demo_cat_follower.json", "default": "True"}, "18": {"name": "test_input", "filename": "./data/defaults/programs/program_test_input.json", "default": "True"}, "19": {"name": "test_output", "filename": "./data/defaults/programs/program_test_output.json", "default": "True"}, "20": {"name": "demo_io_ext", "filename": "./data/defaults/programs/program_demo_io_ext.json", "default": "True"}, "21": {"name": "test_sonars", "filename": "./data/defaults/programs/program_test_sonars.json", "default": "True"}, "22": {"name": "test_led", "dom_code": "ledsicleds6031leds000i1leds11MINUSi1000ADDi5leds000c11005iADDi5MULTIPLYc0MULTIPLYc0MULTIPLYc31leds000", "code": "leds = None\ni = None\nc = None\n\ndef upRange(start, stop, step):\n while start <= stop:\n yield start\n start += abs(step)\n\ndef downRange(start, stop, step):\n while start >= stop:\n yield start\n start -= abs(step)\n\n\nleds = 60\nfor count in range(3):\n get_prog_eng().check_end()\n get_atmega().set_led(1, leds, 0, 0, 0)\n for i in (1 <= float(leds)) and upRange(1, float(leds), 1) or downRange(1, float(leds), 1):\n get_prog_eng().check_end()\n get_atmega().set_led(1, i - 1, 0, 0, 0)\n get_atmega().set_led(i + 5, leds, 0, 0, 0)\n for c in range(1, 101, 5):\n get_prog_eng().check_end()\n get_atmega().set_led(i, i + 5, c * 0, c * 0, c * 3)\n get_atmega().set_led(1, leds, 0, 0, 0)\n", "default": false, "filename": "./data/program_test_led.json"}, "23": {"name": "no_name", "dom_code": "", "code": "", "default": false, "filename": "./data/program_no_name.json"}}} \ No newline at end of file diff --git a/main.py b/main.py index fa80e5bf..17659aa6 100644 --- a/main.py +++ b/main.py @@ -30,6 +30,7 @@ from config import Config from cnn_manager import CNNManager from event import EventManager +from audioControls import AudioCtrl # Logging configuration logger = logging.getLogger() @@ -42,12 +43,12 @@ #logger.addHandler(sh) logger.addHandler(fh) - ## (Connexion) Flask app configuration # Serve a custom version of the swagger ui (Jinja2 templates) based on the default one # from the folder 'swagger-ui'. Clone the 'swagger-ui' repository inside the backend folder -connexionApp = connexion.App(__name__, swagger_ui=True, swagger_path='swagger-ui/') +options = {"swagger_ui": False} +connexionApp = connexion.App(__name__, options=options) # Connexion wraps FlaskApp, so app becomes connexionApp.app app = connexionApp.app @@ -56,7 +57,6 @@ babel = Babel(app) app.debug = False app.prog_engine = ProgramEngine.get_instance() -app.prog = None app.shutdown_requested = False ## New API and web application @@ -153,6 +153,8 @@ def handle_config(): """ Overwrite configuration file on disk and reload it """ + audioCtrl = AudioCtrl.get_instance() + audioCtrl.setVolume(int(request.form['audio_volume_level'])) Config.write(updateDict(app.bot_config, request.form)) app.bot_config = Config.get() return "ok" @@ -176,9 +178,9 @@ def handle_wifi(): psk = request.form.get("wifi_psk") logging.info("mode " + mode +" ssid: " + ssid + " psk: " + psk) - client_params = " \"" + ssid + "\" \"" + psk + "\"" if ssid != "" and psk != "" else "" + client_params = " --ssid \"" + ssid + "\" --pwd \"" + psk + "\"" if ssid != "" and psk != "" else "" logging.info(client_params) - os.system("sudo ./wifi.py updatecfg " + mode + client_params) + os.system("sudo ./wifi.py updatecfg --mode " + mode + client_params) os.system("sudo reboot") if mode == "ap": return "http://coder.bot" @@ -333,8 +335,8 @@ def handle_program_load(): """ logging.debug("program_load") name = request.args.get('name') - app.prog = app.prog_engine.load(name) - return jsonify(app.prog.as_dict()) + prog = app.prog_engine.load(name) + return jsonify(prog.as_dict()) @app.route("/program/save", methods=["POST"]) def handle_program_save(): @@ -367,8 +369,8 @@ def handle_program_exec(): logging.debug("program_exec") name = request.form.get('name') code = request.form.get('code') - app.prog = app.prog_engine.create(name, code) - return json.dumps(app.prog.execute()) + prog = app.prog_engine.create(name, code) + return json.dumps(prog.execute()) @app.route("/program/end", methods=["POST"]) def handle_program_end(): @@ -376,9 +378,9 @@ def handle_program_end(): Stop the program execution """ logging.debug("program_end") - if app.prog: - app.prog.end() - app.prog = None + prog = app.prog_engine.get_current_program() + if prog: + prog.end() return "ok" @app.route("/program/status", methods=["GET"]) @@ -387,9 +389,9 @@ def handle_program_status(): Expose the program status """ logging.debug("program_status") - prog = Program("") - if app.prog: - prog = app.prog + prog = app.prog_engine.get_current_program() + if prog is None: + prog = Program("") return json.dumps({'name': prog.name, "running": prog.is_running(), "log": app.prog_engine.get_log()}) @app.route("/cnnmodels", methods=["GET"]) @@ -446,10 +448,11 @@ def execute(command): def button_pushed(): if app.bot_config.get('button_func') == "startstop": - if app.prog and app.prog.is_running(): - app.prog.end() - elif app.prog and not app.prog.is_running(): - app.prog.execute() + prog = app.prog_engine.get_current_prog() + if prog and prog.is_running(): + prog.end() + elif prog and not prog.is_running(): + prog.execute() def remove_doreset_file(): try: @@ -468,6 +471,10 @@ def run_server(): encoder=bool(app.bot_config.get('encoder'))) audio = Audio.get_instance() audio.say(app.bot_config.get("sound_start")) + + audioCtrl = AudioCtrl.get_instance() + audioCtrl.setVolume(int(app.bot_config.get('audio_volume_level'))) + try: cam = Camera.get_instance() Motion.get_instance() @@ -478,8 +485,8 @@ def run_server(): EventManager.get_instance("coderbot") if app.bot_config.get('load_at_start') and app.bot_config.get('load_at_start'): - app.prog = app.prog_engine.load(app.bot_config.get('load_at_start')) - app.prog.execute() + prog = app.prog_engine.load(app.bot_config.get('load_at_start')) + prog.execute() except ValueError as e: app.bot_config = {} logging.error(e) diff --git a/music.py b/music.py new file mode 100644 index 00000000..0544b468 --- /dev/null +++ b/music.py @@ -0,0 +1,152 @@ +############################################################################ +# CoderBot, a didactical programmable robot. +# Copyright (C) 2014, 2015 Roberto Previtera +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +############################################################################ +# CoderBot, a didactical programmable robot. +# Copyright (C) 2014, 2015 Roberto Previtera +# +# MUSICAL EXTENTION for CoderBot +# This extention is develop by: +# Michele Carbonera - miki_992@hotmail.it - m.carbonera@campus.unimib.it - michele.carbonera@unimib.it +# Antonino Tramontana - a.tramontana1@campus.unimib.it +# Copyright (C) 2020 +############################################################################ + +import os +import sox +import time + +class Music: + _instance = None + managerPackage = None + + noteDict = { + 'C2': -7.0, 'D2' : -5.0, 'E2' : -3.0, 'F2' : -2.0, 'F#2' : -1.0, 'G2' : 0.0, + 'A2' : 2.0, 'Bb2' : 3.0, 'B2' : 4.0, 'C3' : 5.0, 'D3' : 7.0, 'E3' : 9.0, + 'F3' : 10.0, 'G3' : 12.0 + } + + + @classmethod + def get_instance(cls,managerPackage): + if cls._instance is None: + cls._instance = Music(managerPackage) + return cls._instance + + def __init__(self,managerPackage): + + #os.putenv('AUDIODRIVER', 'alsa') + #os.putenv('AUDIODEV', 'hw:1,0') + self.managerPackage = managerPackage + print("We have create a class: MUSICAL") + + def test(self): + tfm = sox.Transformer() + tfm.preview('cat.wav') + tfm.build('cat.wav', 'outMusicDemo.wav') + + #play a pause + # @param duration: duration of the pause in seconds + def play_pause(self, duration): + duration = float(duration) + time.sleep(duration) + + #play a given note for a given instrument + # @param instrument: name of the instrument to be used + # @param note: name of the note in the following format "A2" + # @para alteration: if it is a diesis or a bemolle + # @param time: duration of the note in seconds + def play_note(self, note, instrument='piano', alteration='none', duration=1.0): + print(note) + tfm = sox.Transformer() + + duration = float(duration) + + alt = 0.0 + if alteration == 'bmolle': + alt = -1.0 + elif alteration == 'diesis': + alt = 1.0 + + if note in self.noteDict : + shift = self.noteDict[note]+ alt + else: + print('note not exist') + return + + tfm.pitch(shift, quick=False) + tfm.trim(0.0, end_time=0.5*duration) + if self.managerPackage.isPackageAvailable(instrument): + tfm.preview('./sounds/notes/' + instrument + '/audio.wav') + else: + print("no instrument:"+str(instrument)+" present in this coderbot!") + + def play_animal(self, instrument, note='G2', alteration='none', duration=1.0): + tfm = sox.Transformer() + + duration = float(duration) + + alt = 0.0 + if alteration == 'bmolle': + alt = -1.0 + elif alteration == 'diesis': + alt = 1.0 + + if note == 'C2': + shift = -7.0 + alt + elif note == 'D2': + shift = -5.0 + alt + elif note == 'E2': + shift = -3.0 + alt + elif note == 'F2': + shift = -2.0 + alt + elif note == 'F#2': + shift = -1.0 + alt + elif note == 'G2': + shift = 0.0 + alt + elif note == 'A2': + shift = 2.0 + alt + elif note == 'Bb2': + shift = 3.0 + alt + elif note == 'B2': + shift = 4.0 + alt + elif note == 'C3': + shift = 5.0 + alt + elif note == 'D3': + shift = 7.0 + alt + elif note == 'E3': + shift = 9.0 + alt + elif note == 'F3': + shift = 10.0 + alt + elif note == 'G3': + shift = 12.0 + alt + + if note in self.noteDict : + shift = self.noteDict[note]+ alt + else: + print('note not exist') + return + + if self.managerPackage.isPackageAvailable(instrument): + tfm.preview('./sounds/notes/' + instrument + '/audio.wav') + else: + print("no animal verse:"+str(instrument)+" present in this coderbot!") + return + tfm.pitch(shift, quick=False) + tfm.trim(0.0, end_time=0.5*duration) + #tfm.stretch(time, window=20) + tfm.preview('./sounds/notes/' + instrument + '/audio.wav') diff --git a/musicPackages.py b/musicPackages.py new file mode 100644 index 00000000..051b8738 --- /dev/null +++ b/musicPackages.py @@ -0,0 +1,221 @@ +############################################################################ +# CoderBot, a didactical programmable robot. +# Copyright (C) 2014, 2015 Roberto Previtera +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +############################################################################ +# CoderBot, a didactical programmable robot. +# Copyright (C) 2014, 2015 Roberto Previtera +# +# MUSICAL EXTENTION for CoderBot +# This extention is develop by: +# Michele Carbonera - miki_992@hotmail.it - m.carbonera@campus.unimib.it - michele.carbonera@unimib.it +# Antonino Tramontana - a.tramontana1@campus.unimib.it +# Copyright (C) 2020 +############################################################################ + +import json +import os +import logging +import copy + +class MusicPackage: + + def __init__(self, nameID, category, name_IT, name_EN, version, date): + self.nameID = nameID + self.category = category + self.name_IT = name_IT + self.name_EN = name_EN + self.version = version + self.date = date + self.interfaces = list() + + def getNameID(self): + return self.nameID + + def getCategory(self): + return self.category + + def getNameIT(self): + return self.name_IT + + def getNameEN(self): + return self.name_EN + + def getVersion(self): + return self.version + + def getDate(self): + return self.date + + def getInterfaces(self): + return self.interfaces + + def addInterface(self, musicPackageInterface): + self.interfaces.append(musicPackageInterface) + +class MusicPackageInterface: + + def __init__(self,interfaceName,available,icon): + self.interfaceName = interfaceName + self.available = available + self.icon = icon + + def getInterfaceName(self): + return self.interfaceName + + def getAvailable(self): + return self.available + + def getIcon(self): + return self.icon + +class MusicPackageManager: + _instance = None + + @classmethod + def get_instance(cls): + if cls._instance is None: + cls._instance = MusicPackageManager() + return cls._instance + + def __init__(self): + self.packages = dict() + with open('./sounds/notes/music_package.json') as json_file: + data = json.load(json_file) + for p in data['packages']: + + package = data['packages'][p] + mp = MusicPackage(p,package['category'],package['name_IT'],package['name_EN'],package['version'],package['date']) + for i in package['interface']: + interfaceItem = package['interface'][i] + mpi = MusicPackageInterface(i,interfaceItem['available'],interfaceItem['icon']) + mp.addInterface(mpi) + + if p not in self.packages: + self.packages[p] = mp + + def listPackages(self): + packages_serializable = dict() + for name, package in self.packages.items(): + package_copy = copy.deepcopy(package) + packages_serializable[name] = package_copy.__dict__ + packages_serializable[name]['interfaces'] = [] + for i in package.interfaces: + packages_serializable[name]['interfaces'].append(i.__dict__) + return packages_serializable + + def updatePackages(self): + newdict = { 'packages': {} } + for element in self.packages: + nameID = self.packages[element].getNameID() + newdict['packages'][nameID] = { } + newdict['packages'][nameID]['category']= self.packages[element].getCategory() + newdict['packages'][nameID]['name_IT']= self.packages[element].getNameIT() + newdict['packages'][nameID]['name_EN']= self.packages[element].getNameEN() + newdict['packages'][nameID]['version']= self.packages[element].getVersion() + newdict['packages'][nameID]['date']= self.packages[element].getDate() + newdict['packages'][nameID]['interface']= {'base':{}, 'intermediate':{}, 'advanced': {}} + newdict['packages'][nameID]['interface']['base']['available'] = self.packages[element].getInterfaces()[0].getAvailable() + newdict['packages'][nameID]['interface']['base']['icon'] = self.packages[element].getInterfaces()[0].getIcon() + newdict['packages'][nameID]['interface']['intermediate']['available'] = self.packages[element].getInterfaces()[1].getAvailable() + newdict['packages'][nameID]['interface']['intermediate']['icon'] = self.packages[element].getInterfaces()[1].getIcon() + newdict['packages'][nameID]['interface']['advanced']['available'] = self.packages[element].getInterfaces()[2].getAvailable() + newdict['packages'][nameID]['interface']['advanced']['icon'] = self.packages[element].getInterfaces()[2].getIcon() + + #json_packages = json.dumps(newdict) + with open('sounds/notes/music_package.json', 'w', encoding='utf-8') as json_file: + json.dump(newdict, json_file, ensure_ascii=False, indent=4) + + + def deletePackage(self, packageName): + logging.info("packageName: " + packageName) + if packageName in self.packages: + del self.packages[packageName] + self.updatePackages() + else: + logging.error("errore, il pacchetto " + packageName + " non è stato trovato") + return 2 + + if os.path.exists('./sounds/notes/' + packageName): + os.system('rm -rf ./sounds/notes/' + packageName) + return 1 + + + def verifyVersion(self, packageName, version): + logging.info("verifica pacchetto") + #newversionList = version.split('.') + if packageName not in self.packages: + return True + + newVersionList = [int(x) for x in version.split('.')] + #for i in ragen(0,len(newversionList) -1): + #newversionList[i] = int(newLversionList[i]) + + oldVersion = self.packages[packageName].getVersion() + oldVersionList = [int(x) for x in oldVersion.split('.')] + + for i in range(0,len(newVersionList) -1): + if(newVersionList[i] > oldVersionList[i] ): + return True + elif(newVersionList[i] < oldVersionList[i] ): + return False + + return False + + def addPackage(self, filename): + pkgnames = filename.split('_') + version = pkgnames[1].replace('.zip', '') + logging.info("Music Package version: " + version) + pkgname = pkgnames[0] + pkgpath = './sounds/notes/' + pkgname + if not self.verifyVersion(pkgname, version): + if (version == self.packages[pkgname].getVersion()): + logging.error("errore, il pacchetto " + pkgname + " ha versione identica a quello attualmente installato") + return 3 + else: + logging.info("errore, il pacchetto " + pkgname + " ha versione precendente a quello attualmente installato") + return 2 + else: + + os.system('unzip -o ' + './updatePackages/' + filename + " -d ./updatePackages") + + os.system('mkdir ' + pkgpath) + os.system('mv ./updatePackages/' + pkgname + "/" + 'audio.wav ' + pkgpath + '/') + + with open('./updatePackages/' + pkgname + '/' + pkgname + '.json') as json_file: + logging.info("adding " + pkgname + " package") + data = json.load(json_file) + for p in data['packages']: + package = data['packages'][p] + mp = MusicPackage(p,package['category'],package['name_IT'],package['name_EN'],package['version'],package['date']) + for i in package['interface']: + interfaceItem = package['interface'][i] + mpi = MusicPackageInterface(i,interfaceItem['available'],interfaceItem['icon']) + mp.addInterface(mpi) + + self.packages[p] = mp + + self.updatePackages() + + os.system('rm -rf ./updatePackages/' + pkgname) + return 1 + + + def isPackageAvailable(self,namePackage): + if namePackage in self.packages: + return True + else: + return False diff --git a/photos/0.txt b/photos/0.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/photos/metadata.json b/photos/metadata.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/photos/metadata.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/program.py b/program.py index b66efde9..15629115 100644 --- a/program.py +++ b/program.py @@ -31,13 +31,16 @@ import config import audio import event - - +import music +import musicPackages +import atmega328p PROGRAM_PATH = "./data/" PROGRAM_PREFIX = "program_" PROGRAM_SUFFIX = ".json" +musicPackageManager = musicPackages.MusicPackageManager.get_instance() + def get_cam(): return camera.Camera.get_instance() @@ -56,6 +59,12 @@ def get_prog_eng(): def get_event(): return event.EventManager.get_instance() +def get_music(): + return music.Music.get_instance(musicPackageManager) + +def get_atmega(): + return atmega328p.ATMega328.get_instance() + class ProgramEngine: # pylint: disable=exec-used @@ -105,7 +114,8 @@ def load(self, name): logging.info(program_db_entries[0]) f = open(program_db_entries[0]["filename"], 'r') self._program = Program.from_dict(json.load(f)) - return self._program + return self._program + return None def delete(self, name): query = Query() @@ -131,6 +141,12 @@ def log(self, text): def get_log(self): return self._log + def set_log(self, log): + self._log = "" + + def get_current_program(self): + return self._program + class Program: _running = False @@ -139,21 +155,20 @@ def dom_code(self): return self._dom_code def __init__(self, name, code=None, dom_code=None, default=False): - #super(Program, self).__init__() self._thread = None self.name = name self._dom_code = dom_code self._code = code self._default = default - def execute(self): + def execute(self, options={}): if self._running: raise RuntimeError('already running') + ProgramEngine.get_instance().set_log("") self._running = True - try: - self._thread = threading.Thread(target=self.run) + self._thread = threading.Thread(target=self.run, args=(options,)) self._thread.start() except RuntimeError as re: logging.error("RuntimeError: %s", str(re)) @@ -178,15 +193,16 @@ def is_running(self): def is_default(self): return self._default - def run(self): + def run(self, *args): + options = args[0] try: program = self try: - if config.Config.get().get("prog_video_rec") == "true": - get_cam().video_rec(program.name) + if options.get("autoRecVideo") == True: + get_cam().video_rec(program.name.replace(" ", "_")) logging.debug("starting video") - except Exception: - logging.error("Camera not available") + except Exception as e: + logging.error("Camera not available: " + str(e)) self._log = "" #clear log imports = "import json\n" diff --git a/requirements_stub.txt b/requirements_stub.txt index e8301ef3..6fd067b3 100644 --- a/requirements_stub.txt +++ b/requirements_stub.txt @@ -1,47 +1,49 @@ # Basic set of required packages. If you need to run on a real system (NOT in STUB mode) # install the packages in `requirements.txt` too. -absl-py==0.9.0 +absl-py==1.0.0 astor==0.8.1 -Babel==2.8.0 -certifi==2018.4.16 +Babel==2.10.1 +certifi==2022.5.18.1 chardet==3.0.4 -click==7.0 -clickclick==1.2.2 -connexion==1.4.2 -Flask==1.1.1 -Flask-Babel==0.12.2 -Flask-Cors==3.0.8 -gast==0.2.2 -grpcio==1.26.0 -idna==2.8 -pybind11==2.4.3 -inflection==0.3.1 -itsdangerous==0.24 -Jinja2==2.11.1 -jsonschema==2.6.0 -Markdown==3.1.1 -MarkupSafe==1.1.1 -numpy==1.17.4 -opencv-contrib-python==4.1.1.26 -pigpio==1.45 -Pillow==7.0.0 -protobuf==3.11.3 -Pypubsub==4.0.0 -pytz==2018.4 -pyyaml>=4.2b1 -pyzbar==0.1.7 -requests==2.22.0 -six==1.14.0 -swagger-spec-validator==2.3.1 +click==8.1.3 +clickclick==20.10.2 +connexion==2.13.1 +Flask==2.1.2 +Flask-Babel==2.0.0 +Flask-Cors==3.0.10 +gast==0.5.3 +grpcio==1.46.3 +idna==3.3 +pybind11==2.9.2 +inflection==0.5.1 +itsdangerous==2.1.2 +Jinja2==3.1.2 +jsonschema==4.5.1 +Markdown==3.3.7 +MarkupSafe==2.1.1 +numpy==1.22.4 +opencv-contrib-python==4.5.3.56 +pigpio==1.78 +Pillow==9.1.1 +protobuf==4.21.1 +Pypubsub==4.0.3 +pytz==2022.1 +pyyaml==6.0 +pyzbar==0.1.9 +requests==2.27.1 +six==1.16.0 +swagger-spec-validator==2.7.4 termcolor==1.1.0 -tinydb==3.12.1 -tensorflow==2.1.0 -tensorflow_hub>=0.7.0 -urllib3==1.24.2 -Werkzeug==0.15.3 -setuptools==42.0.1 -smbus2==0.3.0 -spidev==3.4 -cachetools==3.0.0 -pytesseract==0.3.4 +tinydb==4.7.0 +tflite_runtime==2.8.0 +urllib3==1.26.9 +Werkzeug==2.1.2 +setuptools==62.3.2 +smbus2==0.4.1 +spidev==3.5 +cachetools==5.1.0 +sox==1.4.1 +pyalsaaudio==0.9.2 +pytesseract==0.3.9 +sox==1.4.1 diff --git a/sounds/buzz.mp3 b/sounds/buzz.mp3 deleted file mode 100644 index e28d0f69..00000000 Binary files a/sounds/buzz.mp3 and /dev/null differ diff --git a/sounds/buzz.wav b/sounds/buzz.wav new file mode 100644 index 00000000..dae05b95 Binary files /dev/null and b/sounds/buzz.wav differ diff --git a/sounds/i-see-you.mp3 b/sounds/i-see-you.mp3 deleted file mode 100644 index 9965ad52..00000000 Binary files a/sounds/i-see-you.mp3 and /dev/null differ diff --git a/sounds/i-see-you.wav b/sounds/i-see-you.wav new file mode 100644 index 00000000..a3ab3021 Binary files /dev/null and b/sounds/i-see-you.wav differ diff --git a/sounds/notes/cat/audio.wav b/sounds/notes/cat/audio.wav new file mode 100644 index 00000000..eb20db75 Binary files /dev/null and b/sounds/notes/cat/audio.wav differ diff --git a/sounds/notes/dinosaur.wav b/sounds/notes/dinosaur.wav new file mode 100644 index 00000000..4fe28f48 Binary files /dev/null and b/sounds/notes/dinosaur.wav differ diff --git a/sounds/notes/dog/audio.wav b/sounds/notes/dog/audio.wav new file mode 100644 index 00000000..fa3e4e67 Binary files /dev/null and b/sounds/notes/dog/audio.wav differ diff --git a/sounds/notes/duck/audio.wav b/sounds/notes/duck/audio.wav new file mode 100644 index 00000000..ee416bc0 Binary files /dev/null and b/sounds/notes/duck/audio.wav differ diff --git a/sounds/notes/elephant/audio.wav b/sounds/notes/elephant/audio.wav new file mode 100644 index 00000000..7d313846 Binary files /dev/null and b/sounds/notes/elephant/audio.wav differ diff --git a/sounds/notes/flute/audio.wav b/sounds/notes/flute/audio.wav new file mode 100644 index 00000000..1e14fa57 Binary files /dev/null and b/sounds/notes/flute/audio.wav differ diff --git a/sounds/notes/guitar/audio.wav b/sounds/notes/guitar/audio.wav new file mode 100644 index 00000000..673b5127 Binary files /dev/null and b/sounds/notes/guitar/audio.wav differ diff --git a/sounds/notes/music_package.json b/sounds/notes/music_package.json new file mode 100644 index 00000000..d876c5c3 --- /dev/null +++ b/sounds/notes/music_package.json @@ -0,0 +1,193 @@ +{ + "packages": { + "piano": { + "category": "instrument", + "name_IT": "pianoforte", + "name_EN": "piano", + "version": "0.1", + "date": "2020-04-08", + "interface": { + "base": { + "available": "TRUE", + "icon": "piano.png" + }, + "intermediate": { + "available": "TRUE", + "icon": "piano.png" + }, + "advanced": { + "available": "TRUE", + "icon": "piano.png" + } + } + }, + "guitar": { + "category": "instrument", + "name_IT": "chitarra", + "name_EN": "guitar", + "version": "0.1", + "date": "2020-04-08", + "interface": { + "base": { + "available": "TRUE", + "icon": "guitar.png" + }, + "intermediate": { + "available": "TRUE", + "icon": "guitar.png" + }, + "advanced": { + "available": "TRUE", + "icon": "guitar.png" + } + } + }, + "flute": { + "category": "instrument", + "name_IT": "flauto", + "name_EN": "flute", + "version": "0.1", + "date": "2020-04-08", + "interface": { + "base": { + "available": "TRUE", + "icon": "flute.png" + }, + "intermediate": { + "available": "TRUE", + "icon": "flute.png" + }, + "advanced": { + "available": "TRUE", + "icon": "flute.png" + } + } + }, + "cat": { + "category": "animal", + "name_IT": "gatto", + "name_EN": "cat", + "version": "0.1", + "date": "2020-04-08", + "interface": { + "base": { + "available": "TRUE", + "icon": "cat.png" + }, + "intermediate": { + "available": "TRUE", + "icon": "cat.png" + }, + "advanced": { + "available": "TRUE", + "icon": "cat.png" + } + } + }, + "dog": { + "category": "animal", + "name_IT": "cane", + "name_EN": "dog", + "version": "0.1", + "date": "2020-04-08", + "interface": { + "base": { + "available": "TRUE", + "icon": "dog.png" + }, + "intermediate": { + "available": "TRUE", + "icon": "dog.png" + }, + "advanced": { + "available": "TRUE", + "icon": "dog.png" + } + } + }, + "pig": { + "category": "animal", + "name_IT": "maiale", + "name_EN": "pig", + "version": "0.1", + "date": "2020-06-01", + "interface": { + "base": { + "available": "TRUE", + "icon": "pig.png" + }, + "intermediate": { + "available": "TRUE", + "icon": "pig.png" + }, + "advanced": { + "available": "TRUE", + "icon": "pig.png" + } + } + }, + "elephant": { + "category": "animal", + "name_IT": "elefante", + "name_EN": "elephant", + "version": "0.1", + "date": "2020-06-01", + "interface": { + "base": { + "available": "TRUE", + "icon": "elephant.png" + }, + "intermediate": { + "available": "TRUE", + "icon": "elephant.png" + }, + "advanced": { + "available": "TRUE", + "icon": "elephant.png" + } + } + }, + "snake": { + "category": "animal", + "name_IT": "serpente", + "name_EN": "snake", + "version": "0.1", + "date": "2020-06-01", + "interface": { + "base": { + "available": "TRUE", + "icon": "snake.png" + }, + "intermediate": { + "available": "TRUE", + "icon": "snake.png" + }, + "advanced": { + "available": "TRUE", + "icon": "snake.png" + } + } + }, + "duck": { + "category": "animal", + "name_IT": "anatra", + "name_EN": "duck", + "version": "0.1", + "date": "2020-06-01", + "interface": { + "base": { + "available": "TRUE", + "icon": "duck.png" + }, + "intermediate": { + "available": "TRUE", + "icon": "duck.png" + }, + "advanced": { + "available": "TRUE", + "icon": "duck.png" + } + } + } + } +} diff --git a/sounds/notes/piano/audio.wav b/sounds/notes/piano/audio.wav new file mode 100644 index 00000000..5eaabb67 Binary files /dev/null and b/sounds/notes/piano/audio.wav differ diff --git a/sounds/notes/pig/audio.wav b/sounds/notes/pig/audio.wav new file mode 100644 index 00000000..63ddcf4c Binary files /dev/null and b/sounds/notes/pig/audio.wav differ diff --git a/sounds/notes/snake/audio.wav b/sounds/notes/snake/audio.wav new file mode 100644 index 00000000..39775e14 Binary files /dev/null and b/sounds/notes/snake/audio.wav differ diff --git a/sounds/phaser.mp3 b/sounds/phaser.mp3 deleted file mode 100644 index 632a307b..00000000 Binary files a/sounds/phaser.mp3 and /dev/null differ diff --git a/sounds/phaser.wav b/sounds/phaser.wav new file mode 100644 index 00000000..bc3a59ff Binary files /dev/null and b/sounds/phaser.wav differ diff --git a/sounds/scanner.mp3 b/sounds/scanner.mp3 deleted file mode 100644 index 534647f3..00000000 Binary files a/sounds/scanner.mp3 and /dev/null differ diff --git a/sounds/scanner.wav b/sounds/scanner.wav new file mode 100644 index 00000000..9212f29a Binary files /dev/null and b/sounds/scanner.wav differ diff --git a/sounds/shutdown.mp3 b/sounds/shutdown.mp3 deleted file mode 100644 index 0039e395..00000000 Binary files a/sounds/shutdown.mp3 and /dev/null differ diff --git a/sounds/shutdown.wav b/sounds/shutdown.wav new file mode 100644 index 00000000..73627427 Binary files /dev/null and b/sounds/shutdown.wav differ diff --git a/sounds/shutter.mp3 b/sounds/shutter.mp3 deleted file mode 100644 index b376d7c3..00000000 Binary files a/sounds/shutter.mp3 and /dev/null differ diff --git a/sounds/shutter.wav b/sounds/shutter.wav new file mode 100644 index 00000000..96bc89c6 Binary files /dev/null and b/sounds/shutter.wav differ diff --git a/sounds/startup.mp3 b/sounds/startup.mp3 deleted file mode 100644 index a33852fe..00000000 Binary files a/sounds/startup.mp3 and /dev/null differ diff --git a/sounds/startup.wav b/sounds/startup.wav new file mode 100644 index 00000000..707ace6b Binary files /dev/null and b/sounds/startup.wav differ diff --git a/sounds/still-there.mp3 b/sounds/still-there.mp3 deleted file mode 100644 index 9bd92881..00000000 Binary files a/sounds/still-there.mp3 and /dev/null differ diff --git a/sounds/still-there.wav b/sounds/still-there.wav new file mode 100644 index 00000000..35c2f01b Binary files /dev/null and b/sounds/still-there.wav differ diff --git a/sounds/there-you-are.mp3 b/sounds/there-you-are.mp3 deleted file mode 100644 index 92165bc8..00000000 Binary files a/sounds/there-you-are.mp3 and /dev/null differ diff --git a/sounds/there-you-are.wav b/sounds/there-you-are.wav new file mode 100644 index 00000000..bf18c808 Binary files /dev/null and b/sounds/there-you-are.wav differ diff --git a/start.sh b/start.sh index a8ab92dd..b628acf2 100755 --- a/start.sh +++ b/start.sh @@ -1,2 +1,3 @@ #!/bin/bash -LD_PRELOAD=/usr/lib/arm-linux-gnueabihf/libatomic.so.1.2.0 python3 init.py +[[ -d "firmware" ]] && [[ ! -f "firmware/initialised" ]] && source firmware/upload.sh +AUDIODEV=hw:1 LD_PRELOAD=/usr/lib/arm-linux-gnueabihf/libatomic.so.1.2.0 python3 init.py diff --git a/static/js/blockly/blocks.js b/static/js/blockly/blocks.js index 5dd1c305..3602c871 100644 --- a/static/js/blockly/blocks.js +++ b/static/js/blockly/blocks.js @@ -1206,3 +1206,115 @@ Blockly.Python['coderbot_mpu_get_temp'] = function(block) { var code = 'get_bot().get_mpu_temp()'; return [code, Blockly.Python.ORDER_ATOMIC]; }; + +Blockly.Blocks['coderbot_atmega_get_input'] = { + /** + * Block for get_input function. + * @this Blockly.Block + */ + init: function() { + this.setHelpUrl(Blockly.Msg.LOGIC_BOOLEAN_HELPURL); + this.setColour(240); + this.appendDummyInput() + .appendField(Blockly.Msg.CODERBOT_ATMEGA_READ) + .appendField(new Blockly.FieldDropdown([[Blockly.Msg.CODERBOT_ATMEGA_AI_1, "0"], + [Blockly.Msg.CODERBOT_ATMEGA_AI_2, "1"], + [Blockly.Msg.CODERBOT_ATMEGA_DI_3, "2"], + [Blockly.Msg.CODERBOT_ATMEGA_DI_4, "3"], + [Blockly.Msg.CODERBOT_ATMEGA_DI_5, "4"], + [Blockly.Msg.CODERBOT_ATMEGA_DI_6, "5"],]), 'INPUT'); + this.setOutput(true, 'Number'); + this.setTooltip(Blockly.Msg.LOGIC_BOOLEAN_TOOLTIP); + } +}; + +Blockly.Python['coderbot_atmega_get_input'] = function(block) { + // input index: 0, 1 are Analogs, 2..5 are Digital + var input = block.getFieldValue('INPUT'); + var code = 'get_atmega().get_input(' + input + ')'; + return [code, Blockly.Python.ORDER_ATOMIC]; +}; + +Blockly.Blocks['coderbot_atmega_set_output'] = { + /** + * Block for set_output function. + * @this Blockly.Block + */ + init: function() { + this.setHelpUrl(Blockly.Msg.LOGIC_BOOLEAN_HELPURL); + this.setColour(240); + this.appendDummyInput() + .appendField(Blockly.Msg.CODERBOT_ATMEGA_WRITE) + .appendField(new Blockly.FieldDropdown([[Blockly.Msg.CODERBOT_ATMEGA_DO_1, "0"], + [Blockly.Msg.CODERBOT_ATMEGA_DO_2, "1"], + [Blockly.Msg.CODERBOT_ATMEGA_DO_3, "2"], + [Blockly.Msg.CODERBOT_ATMEGA_DO_4, "3"], + [Blockly.Msg.CODERBOT_ATMEGA_DO_5, "4"], + [Blockly.Msg.CODERBOT_ATMEGA_DO_6, "5"], + [Blockly.Msg.CODERBOT_ATMEGA_DO_7, "6"], + [Blockly.Msg.CODERBOT_ATMEGA_DO_8, "7"], + [Blockly.Msg.CODERBOT_ATMEGA_DO_9, "8"], + [Blockly.Msg.CODERBOT_ATMEGA_DO_10, "9"], + [Blockly.Msg.CODERBOT_ATMEGA_DO_11, "10"],]), 'OUTPUT'); + this.appendValueInput('VALUE') + .setCheck('Boolean') + .appendField(Blockly.Msg.CODERBOT_ATMEGA_VALUE); + this.setInputsInline(true); + this.setPreviousStatement(true); + this.setNextStatement(true); + this.setTooltip(Blockly.Msg.LOGIC_BOOLEAN_TOOLTIP); + } +}; + +Blockly.Python['coderbot_atmega_set_output'] = function(block) { + // input index: 0, 10 are Digital + var output = block.getFieldValue('OUTPUT'); + var value = Blockly.Python.valueToCode(block, 'VALUE', + Blockly.Python.ORDER_NONE) || '\'\''; + var code = 'get_atmega().set_output(' + output + ', ' + value + ')\n'; + return code; +}; + +Blockly.Blocks['coderbot_atmega_set_led'] = { + /** + * Block for set_output function. + * @this Blockly.Block + */ + init: function() { + this.setHelpUrl(Blockly.Msg.LOGIC_BOOLEAN_HELPURL); + this.setColour(240); + this.appendDummyInput() + .appendField(Blockly.Msg.CODERBOT_ATMEGA_LED_SET) + this.appendValueInput('BEGIN') + .setCheck('Number') + .appendField(Blockly.Msg.CODERBOT_ATMEGA_LED_BEGIN); + this.appendValueInput('END') + .setCheck('Number') + .appendField(Blockly.Msg.CODERBOT_ATMEGA_LED_END); + this.appendValueInput('RED') + .setCheck('Number') + .appendField(Blockly.Msg.CODERBOT_ATMEGA_LED_RED); + this.appendValueInput('GREEN') + .setCheck('Number') + .appendField(Blockly.Msg.CODERBOT_ATMEGA_LED_GREEN); + this.appendValueInput('BLUE') + .setCheck('Number') + .appendField(Blockly.Msg.CODERBOT_ATMEGA_LED_BLUE); + this.setInputsInline(true); + this.setPreviousStatement(true); + this.setNextStatement(true); + this.setTooltip(Blockly.Msg.LOGIC_BOOLEAN_TOOLTIP); + } +}; + +Blockly.Python['coderbot_atmega_set_led'] = function(block) { + // input index: 0, 10 are Digital + var begin = Blockly.Python.valueToCode(block, 'BEGIN', Blockly.Python.ORDER_NONE); + var end = Blockly.Python.valueToCode(block, 'END', Blockly.Python.ORDER_NONE); + var red = Blockly.Python.valueToCode(block, 'RED', Blockly.Python.ORDER_NONE); + var green = Blockly.Python.valueToCode(block, 'GREEN', Blockly.Python.ORDER_NONE); + var blue = Blockly.Python.valueToCode(block, 'BLUE', Blockly.Python.ORDER_NONE); + var code = 'get_atmega().set_led(' + begin + ', ' + end + ', ' + red + ', ' + green + ', ' + blue + ')\n'; + + return code; +}; diff --git a/static/js/blockly/bot_en.js b/static/js/blockly/bot_en.js index bc436856..31d77c4c 100644 --- a/static/js/blockly/bot_en.js +++ b/static/js/blockly/bot_en.js @@ -97,4 +97,24 @@ Blockly.Msg.CODERBOT_EVENT_PUBLISH = "publish"; Blockly.Msg.CODERBOT_EVENT_ON_TOPIC = "on topic"; Blockly.Msg.CODERBOT_EVENT_GENERATOR = "event generator"; Blockly.Msg.CODERBOT_CONVERSATION_PARSE = "parse"; +Blockly.Msg.CODERBOT_ATMEGA_READ = "Read"; +Blockly.Msg.CODERBOT_ATMEGA_VALUE = "Value"; +Blockly.Msg.CODERBOT_ATMEGA_AI_1 = "Analog Input 1"; +Blockly.Msg.CODERBOT_ATMEGA_AI_2 = "Analog Input 2"; +Blockly.Msg.CODERBOT_ATMEGA_DI_3 = "Digital Input 1"; +Blockly.Msg.CODERBOT_ATMEGA_DI_4 = "Digital Input 2"; +Blockly.Msg.CODERBOT_ATMEGA_DI_5 = "Digital Input3"; +Blockly.Msg.CODERBOT_ATMEGA_DI_6 = "Digital Input 4"; +Blockly.Msg.CODERBOT_ATMEGA_WRITE = "Write"; +Blockly.Msg.CODERBOT_ATMEGA_DO_1 = "Digital Output 1"; +Blockly.Msg.CODERBOT_ATMEGA_DO_2 = "Digital Output 2"; +Blockly.Msg.CODERBOT_ATMEGA_DO_3 = "Digital Output 3"; +Blockly.Msg.CODERBOT_ATMEGA_DO_4 = "Digital Output 4"; +Blockly.Msg.CODERBOT_ATMEGA_DO_5 = "Digital Output 5"; +Blockly.Msg.CODERBOT_ATMEGA_DO_6 = "Digital Output 6"; +Blockly.Msg.CODERBOT_ATMEGA_DO_7 = "Digital Output 7"; +Blockly.Msg.CODERBOT_ATMEGA_DO_8 = "Digital Output 8"; +Blockly.Msg.CODERBOT_ATMEGA_DO_9 = "Digital Output 9"; +Blockly.Msg.CODERBOT_ATMEGA_DO_10 = "Digital Output 10"; +Blockly.Msg.CODERBOT_ATMEGA_DO_11 = "Digital Output 11"; diff --git a/static/js/blockly/bot_it.js b/static/js/blockly/bot_it.js index 0d7c70e2..d8492b0a 100644 --- a/static/js/blockly/bot_it.js +++ b/static/js/blockly/bot_it.js @@ -97,3 +97,29 @@ Blockly.Msg.CODERBOT_EVENT_PUBLISH = "pubblica"; Blockly.Msg.CODERBOT_EVENT_ON_TOPIC = "sul topic"; Blockly.Msg.CODERBOT_EVENT_GENERATOR = "genera eventi"; Blockly.Msg.CODERBOT_CONVERSATION_PARSE = "interpreta"; +Blockly.Msg.CODERBOT_ATMEGA_READ = "Leggi"; +Blockly.Msg.CODERBOT_ATMEGA_VALUE = "Valore"; +Blockly.Msg.CODERBOT_ATMEGA_AI_1 = "Analog Input 1"; +Blockly.Msg.CODERBOT_ATMEGA_AI_2 = "Analog Input 2"; +Blockly.Msg.CODERBOT_ATMEGA_DI_3 = "Digital Input 1"; +Blockly.Msg.CODERBOT_ATMEGA_DI_4 = "Digital Input 2"; +Blockly.Msg.CODERBOT_ATMEGA_DI_5 = "Digital Input3"; +Blockly.Msg.CODERBOT_ATMEGA_DI_6 = "Digital Input 4"; +Blockly.Msg.CODERBOT_ATMEGA_WRITE = "Scrivi"; +Blockly.Msg.CODERBOT_ATMEGA_DO_1 = "Digital Output 1"; +Blockly.Msg.CODERBOT_ATMEGA_DO_2 = "Digital Output 2"; +Blockly.Msg.CODERBOT_ATMEGA_DO_3 = "Digital Output 3"; +Blockly.Msg.CODERBOT_ATMEGA_DO_4 = "Digital Output 4"; +Blockly.Msg.CODERBOT_ATMEGA_DO_5 = "Digital Output 5"; +Blockly.Msg.CODERBOT_ATMEGA_DO_6 = "Digital Output 6"; +Blockly.Msg.CODERBOT_ATMEGA_DO_7 = "Digital Output 7"; +Blockly.Msg.CODERBOT_ATMEGA_DO_8 = "Digital Output 8"; +Blockly.Msg.CODERBOT_ATMEGA_DO_9 = "Digital Output 9"; +Blockly.Msg.CODERBOT_ATMEGA_DO_10 = "Digital Output 10"; +Blockly.Msg.CODERBOT_ATMEGA_DO_11 = "Digital Output 11"; +Blockly.Msg.CODERBOT_ATMEGA_LED_SET = "Controlla Led"; +Blockly.Msg.CODERBOT_ATMEGA_LED_BEGIN = "Led inizio"; +Blockly.Msg.CODERBOT_ATMEGA_LED_END = "Led fine"; +Blockly.Msg.CODERBOT_ATMEGA_LED_RED = "Intensità Rosso"; +Blockly.Msg.CODERBOT_ATMEGA_LED_GREEN = "Intensità Verde"; +Blockly.Msg.CODERBOT_ATMEGA_LED_BLUE = "Intensità Blu"; diff --git a/templates/blocks_adv.xml b/templates/blocks_adv.xml index 2f2a2030..4a3f3c70 100644 --- a/templates/blocks_adv.xml +++ b/templates/blocks_adv.xml @@ -331,6 +331,11 @@ + + + + + diff --git a/test/musicPackage_test.py b/test/musicPackage_test.py new file mode 100644 index 00000000..65f4823f --- /dev/null +++ b/test/musicPackage_test.py @@ -0,0 +1,41 @@ +#__import__("../musicPackages") +import json +import sys +sys.path.insert(0, './') +import musicPackages +class MusicPackage_test: + + def test_musicPackage(self): + print("sample Music Package: ") + print(" name_IT = name_it, name_EN = name_en , category = sample_category, version = sample_version, date = sample_date, interfaces = sample_interfaces, nameID = sample_id") + + mpkg = musicPackages.MusicPackage(name_IT = "name_it", name_EN = "name_en", category= "sample_category", version= "sample_version", date="sample_date", nameID="sample_id") + print("name_IT : ", mpkg.getNameIT()) + print("name_EN : ", mpkg.getNameEN()) + print("nameID : ", mpkg.getNameID()) + print("version : ", mpkg.getVersion()) + print("date : ", mpkg.getDate()) + print("category : ", mpkg.getCategory()) + print("interfaces : ", mpkg.getInterfaces()) + + def test_isPackageAvaible(self): + pkg_manager = musicPackages.MusicPackageManager() + for package_name in pkg_manager.packages: + print("Test if " + package_name + " package is available") + result = pkg_manager.isPackageAvailable(package_name) + if(result): + print(package_name + " package is available") + else: + print(package_name + " package is not available") + + print("Test if NONE package is available" ) + result = pkg_manager.isPackageAvailable("NONE") + if(result): + print("NONE package is available") + else: + print("NONE package is not available") + +test = MusicPackage_test() +test.test_musicPackage() +test.test_isPackageAvaible() + diff --git a/test/music_test.py b/test/music_test.py new file mode 100644 index 00000000..b73a6b1b --- /dev/null +++ b/test/music_test.py @@ -0,0 +1,116 @@ +import sys +import sox +import time +import os +sys.path.insert(0, './') +from musicPackages import MusicPackageManager +from music import Music + +class Music_test: + + def test_library(self): + print("testing sound playback:...") + tfm = sox.Transformer() + tfm.preview('cat.wav') + tfm.build('cat.wav', 'outMusicDemo.wav') + +# test each parametr of the function play_note + def test_play_note(self): + musicPkg = MusicPackageManager() + m = Music(musicPkg) + print('test Music.play_note') + print("m.play_note(note='C2')") + m.play_note(note='C2') + print("m.play_note(note='C2',duration=2.0)") + m.play_note(note='C2',duration=2.0) + print("m.play_note(note='C2',instrument='guitar')") + m.play_note(note='C2',instrument='guitar') + print("m.play_note(note='C2',alteration='bmolle')") + m.play_note(note='C2',alteration='bmolle') + print("m.play_note(note='C2',alteration='diesis')") + m.play_note(note='C2',alteration='diesis') + print("m.play_note(note='C2',instrument='guitar')") + m.play_note(note='C2',instrument='guitar') + print("m.play_note(note='C2',alteration='bmolle')") + m.play_note(note='C2',alteration='bmolle') + print("m.play_note(note='C2',instrument='guitar',alteration='diesis')") + m.play_note(note='C2',instrument='guitar',alteration='diesis') + print("m.play_note(note='C2',instrument='guitar',alteration='diesis',duration=2.0)") + m.play_note(note='C2',instrument='guitar',alteration='diesis',duration=2.0) + print("m.play_note(note='G3',duration=2.0)") + m.play_note(note='G3',duration=2.0) + print("m.play_note(note='G3',instrument='guitar')") + m.play_note(note='G3',instrument='guitar') + print("m.play_note(note='G3',alteration='bmolle')") + m.play_note(note='G3',alteration='bmolle') + print("m.play_note(note='G3',alteration='diesis')") + m.play_note(note='G3',alteration='diesis') + print("m.play_note(note='G3',instrument='guitar')") + m.play_note(note='G3',instrument='guitar') + print("m.play_note(note='G3',alteration='bmolle')") + m.play_note(note='G3',alteration='bmolle') + print("m.play_note(note='G3',instrument='guitar',alteration='diesis')") + m.play_note(note='G3',instrument='guitar',alteration='diesis') + print("m.play_note(note='G3',instrument='guitar',alteration='diesis',duration=2.0)") + m.play_note(note='G3',instrument='guitar',alteration='diesis',duration=2.0) + print("it's ok if print: no instrument: coderInstrument present in this coderbot!") + m.play_note(note='C2',instrument='coderInstrument',alteration='diesis',duration=2.0) + print("it's ok if print: note: coderNote not exist") + m.play_note(note='coderNote',instrument='piano',alteration='diesis',duration=2.0) + + +# test each parametr of the function play_note + def test_play_animal(self): + print('test Music.play_animal') + musicPkg = MusicPackageManager() + m = Music(musicPkg) + print("(note='C2',instrument='cat', duration=2.0)") + m.play_animal(note='C2',instrument='cat', duration=2.0) + print("m.play_animal(note='C2',instrument='dog')") + m.play_animal(note='C2',instrument='dog') + print("m.play_animal(note='C2',instrument='dog', alteration='bmolle')") + m.play_animal(note='C2',instrument='dog', alteration='bmolle') + print("m.play_animal(note='C2',instrument='cat', alteration='diesis')") + m.play_animal(note='C2',instrument='cat', alteration='diesis') + print("m.play_animal(note='C2',instrument='dinosaur')") + m.play_animal(note='C2',instrument='dinosaur') + print("m.play_animal(note='C2',alteration='bmolle')") + m.play_animal(note='C2',instrument="dinosaur", alteration='bmolle') + print("m.play_animal(note='C2',instrument='cat',alteration='diesis')") + m.play_animal(note='C2',instrument='cat',alteration='diesis') + print("m.play_animal(note='C2',instrument='cat',alteration='diesis',duration=2.0)") + m.play_animal(note='C2',instrument='cat',alteration='diesis',duration=2.0) + print("m.play_note(note='G3',duration=2.0)") + m.play_note(note='G3',duration=2.0) + print("m.play_note(note='G3',instrument='dinosaur',alteration='bmolle')") + m.play_note(note='G3',instrument='dinosaur',alteration='bmolle') + print("m.play_note(note='G3',instrument='dinosaur',alteration='diesis')") + m.play_note(note='G3',instrument='dinosaur',alteration='diesis') + print("m.play_note(note='G3',alteration='bmolle', instrument= 'cat')") + m.play_note(note='G3',alteration='bmolle', instrument= 'cat') + print("m.play_note(note='G3',instrument='cat',alteration='diesis')") + m.play_note(note='G3',instrument='cat',alteration='diesis') + print("m.play_note(note='G3',instrument='cat',alteration='diesis',duration=2.0)") + m.play_note(note='G3',instrument='cat',alteration='diesis',duration=2.0) + print("it's ok if print: no instrument: coderInstrument present in this coderbot!") + m.play_animal(note='C2',instrument='coderInstrument',alteration='diesis',duration=2.0) + print("it's ok if print: note: coderNote not exist") + m.play_animal(note='coderNote',instrument='cat',alteration='diesis',duration=2.0) + print('test Music.play_note: ENDED') + + + def test_play_pause(self): + print('test Music.play_pause') + musicPkg = MusicPackageManager() + m = Music() + prrint("play pause") + m.play_pause(1.0) + prrint("play pause and note") + m.play_note(note='C2',instrument='guitar') + m.play_pause(2.0) + m.play_note(note='C2',instrument='guitar') + +test = Music_test() +test.test_play_note() +test.test_play_animal() + diff --git a/v2.yml b/v2.yml index 803cac67..b09d8871 100644 --- a/v2.yml +++ b/v2.yml @@ -1,6 +1,6 @@ swagger: "2.0" info: - version: "0.2" + version: "0.3" title: OpenAPI 2.0 definition of Coderbot API v2 consumes: @@ -74,9 +74,35 @@ paths: description: "ok" 400: description: "Failed to save the activity" + /listMusicPackages: + get: + operationId: "api.listMusicPackages" + summary: "List Music Packages" + responses: + 200: + description: "ok" + /deleteMusicPackage: + post: + operationId: "api.deleteMusicPackage" + summary: "Delete Music Package" + parameters: + - name: package_data + in: body + schema: + type: object + properties: + package_name: + type: string + responses: + 200: + description: "ok" + 400: + description: "not found" + /updateFromPackage: post: operationId: "api.updateFromPackage" + summary: "Update CoderBot from package" consumes: - multipart/form-data parameters: @@ -120,7 +146,11 @@ paths: - name: name in: query type: string - required: true + required: false + - name: default + in: query + type: string + required: false tags: - Activity management responses: @@ -133,10 +163,6 @@ paths: tags: - Program management parameters: - - name: overwrite - in: query - required: false - type: string - in: body name: data schema: @@ -191,7 +217,7 @@ paths: description: Components names to be tested schema: type: object - default: {'varargin': ['motors', 'sonar', 'speaker', 'ocr']} + # default: {'varargin': ['motors', 'sonar', 'speaker', 'ocr']} required: - varargin properties: @@ -232,7 +258,7 @@ paths: description: Movement speed and duration schema: type: object - default: {'speed': 100, 'elapse':0, 'distance':0} + # default: {'speed': 100, 'elapse':0, 'distance':0} required: - speed - elapse @@ -276,3 +302,13 @@ paths: responses: 200: description: Sent command to the bot GPIO. + /listCNNModels: + get: + operationId: "api.list_cnn_models" + summary: "list of CNN Models" + tags: + - CNN Models + responses: + 200: + description: "CNN Models as JSON Object" + diff --git a/wifi.py b/wifi.py index 735db240..3bc5101e 100755 --- a/wifi.py +++ b/wifi.py @@ -91,7 +91,7 @@ def get_ipaddr(cls, ifname): return socket.inet_ntoa(fcntl.ioctl( s.fileno(), 0x8915, # SIOCGIFADDR - struct.pack('256s', ifname[:15]) + struct.pack('256s', ifname.encode('utf-8')[:15]) )[20:24]) @classmethod @@ -153,7 +153,11 @@ def start_as_client(cls): time.sleep(1.0) out = os.system("wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf > /dev/null 2>&1") out += os.system("dhclient -1 wlan0") - print(out) + print("start_as_client: " + str(out)) + ipaddr = cls.get_ipaddr("wlan0") + if ipaddr is None or "169.254" in ipaddr: + os.system("sudo pkill wpa_supplicant") + raise Exception() try: cls.register_ipaddr(cls.get_macaddr("wlan0"), cls.get_config().get('bot_name', 'CoderBot'), cls.get_ipaddr("wlan0"), "roberto.previtera@gmail.com") print("registered bot, ip: " + str(cls.get_ipaddr("wlan0") + " name: " + cls.get_config().get('bot_name', 'CoderBot'))) @@ -175,7 +179,7 @@ def start_as_ap(cls): out += str(subprocess.check_output(["ip", "a", "add", "10.0.0.1/24", "dev", "wlan0"])) out += str(subprocess.check_output(["ip", "link", "set", "dev", "wlan0", "up"])) out += str(subprocess.check_output(["ifconfig"])) - print(out) + print("start_as_ap: " + str(out)) cls.start_hostapd() cls.start_dnsmasq() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy