#!/bin/bash # ModuleAir Pro 4G - Complete Installation Script # Author: ModuleAir Team # Description: Automated deployment script for Raspberry Pi with colored output set -e # Exit on any error # Color codes for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' WHITE='\033[1;37m' NC='\033[0m' # No Color # Function to print colored messages print_status() { echo -e "${BLUE}[INFO]${NC} $1" } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } print_header() { echo -e "${PURPLE}================================${NC}" echo -e "${WHITE}$1${NC}" echo -e "${PURPLE}================================${NC}" } print_step() { echo -e "${CYAN}[STEP]${NC} $1" } # Check if running as root check_root() { if [[ $EUID -eq 0 ]]; then print_error "This script should not be run as root. Run as regular user with sudo privileges." exit 1 fi } # Check if sudo is available check_sudo() { print_step "Checking sudo privileges..." if ! sudo -n true 2>/dev/null; then print_error "This script requires sudo privileges. Please run: sudo visudo" exit 1 fi print_success "Sudo privileges confirmed" } # Pre-configure packages to avoid prompts pre_configure_packages() { print_step "Pre-configuring packages to avoid prompts..." # Pre-configure common package questions # For any package that uses debconf echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections # For timezone data (if tzdata gets updated) echo 'tzdata tzdata/Areas select Europe' | sudo debconf-set-selections echo 'tzdata tzdata/Zones/Europe select Paris' | sudo debconf-set-selections # For keyboard configuration echo 'keyboard-configuration keyboard-configuration/layoutcode string us' | sudo debconf-set-selections # For any service restart prompts echo '* libraries/restart-without-asking boolean true' | sudo debconf-set-selections print_success "Package pre-configuration completed" } # System update update_system() { print_step "Updating system packages..." # Set non-interactive mode for apt export DEBIAN_FRONTEND=noninteractive sudo apt update -y print_success "System packages updated" } # Install dependencies install_dependencies() { print_step "Installing system dependencies..." # Ensure non-interactive mode export DEBIAN_FRONTEND=noninteractive local packages=( "git" "apache2" "php" "php-sqlite3" "sqlite3" "python3" "python3-pip" "python3-dev" "build-essential" "cmake" "libsqlite3-dev" "i2c-tools" "python3-smbus" "python3-serial" "python3-requests" "python3-schedule" "python3-rpi.gpio" ) for package in "${packages[@]}"; do print_status "Installing $package..." if sudo DEBIAN_FRONTEND=noninteractive apt install -y -q "$package"; then print_success "$package installed" else print_error "Failed to install $package" exit 1 fi done print_success "All system dependencies installed" } # Install Python packages install_python_packages() { print_step "Installing Python packages..." local pip_packages=( "pymodbus" "pyserial" "requests" "schedule" "gpiozero" "smbus2" "adafruit-circuitpython-bme280" "sensirion-shdlc-sfa3x" "crcmod" "psutil" ) for package in "${pip_packages[@]}"; do print_status "Installing Python package: $package..." if sudo pip3 install "$package" --break-system-packages; then print_success "$package installed" else print_warning "Failed to install $package (may already be installed)" fi done print_success "Python packages installation completed" } # Clone repository clone_repository() { print_step "Cloning ModuleAir Pro 4G repository..." # Remove existing directory if it exists if [ -d "/var/www/moduleair_pro_4g" ]; then print_warning "Directory /var/www/moduleair_pro_4g already exists - removing..." sudo rm -rf /var/www/moduleair_pro_4g fi # Clone the repository if sudo git clone http://gitea.aircarto.fr/PaulVua/moduleair_pro_4g.git /var/www/moduleair_pro_4g; then print_success "Repository cloned successfully" else print_error "Failed to clone repository" exit 1 fi # Set proper permissions sudo chown -R www-data:www-data /var/www/moduleair_pro_4g sudo chmod -R 755 /var/www/moduleair_pro_4g print_success "Repository permissions set" } # Configure Apache configure_apache() { print_step "Configuring Apache web server..." # Backup original config sudo cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/000-default.conf.backup print_status "Original Apache config backed up" # Update DocumentRoot sudo sed -i 's|DocumentRoot /var/www/html|DocumentRoot /var/www/moduleair_pro_4g|' /etc/apache2/sites-available/000-default.conf # Enable PHP if not already enabled sudo a2enmod php* 2>/dev/null || true # Restart Apache sudo systemctl restart apache2 sudo systemctl enable apache2 print_success "Apache configured and restarted" } # System optimizations (disable audio, isolate CPU) system_optimizations() { print_step "Applying system optimizations..." # Find config.txt location local config_file="/boot/firmware/config.txt" if [ ! -f "$config_file" ]; then config_file="/boot/config.txt" fi if [ ! -f "$config_file" ]; then print_error "Could not find config.txt file" return 1 fi # Disable onboard audio print_status "Disabling onboard audio..." if grep -q "^dtparam=audio=" "$config_file"; then # Replace existing audio parameter sudo sed -i 's/^dtparam=audio=.*/dtparam=audio=off/' "$config_file" print_success "Audio parameter updated to off" else # Add audio parameter if not exists echo "dtparam=audio=off" | sudo tee -a "$config_file" > /dev/null print_success "Audio disabled in config.txt" fi # Blacklist audio module print_status "Blacklisting audio module..." echo "blacklist snd_bcm2835" | sudo tee /etc/modprobe.d/blacklist.conf > /dev/null print_success "Audio module blacklisted" # Find cmdline.txt location local cmdline_file="/boot/firmware/cmdline.txt" if [ ! -f "$cmdline_file" ]; then cmdline_file="/boot/cmdline.txt" fi if [ ! -f "$cmdline_file" ]; then print_error "Could not find cmdline.txt file" return 1 fi # Add CPU isolation print_status "Setting up CPU isolation..." if ! grep -q "isolcpus=3" "$cmdline_file"; then # Read current cmdline and append isolcpus local current_cmdline=$(cat "$cmdline_file") echo "${current_cmdline} isolcpus=3" | sudo tee "$cmdline_file" > /dev/null print_success "CPU core 3 isolated" else print_warning "CPU isolation already configured" fi # Check if audio module is currently loaded if lsmod | grep -q snd_bcm2835; then print_warning "Audio module is currently loaded. It will be disabled after reboot." else print_success "Audio module is not loaded" fi print_success "System optimizations completed" } # Enable hardware interfaces enable_hardware() { print_step "Enabling hardware interfaces..." # Enable I2C using raspi-config print_status "Enabling I2C interface..." if sudo raspi-config nonint do_i2c 0; then print_success "I2C enabled via raspi-config" else print_warning "Failed to enable I2C via raspi-config" fi # Enable SPI using raspi-config print_status "Enabling SPI interface..." if sudo raspi-config nonint do_spi 0; then print_success "SPI enabled via raspi-config" else print_warning "Failed to enable SPI via raspi-config" fi # Enable Serial/UART using raspi-config print_status "Enabling Serial interface..." if sudo raspi-config nonint do_serial 0; then print_success "Serial enabled via raspi-config" else print_warning "Failed to enable Serial via raspi-config" fi # Check if config.txt exists for additional UART configurations local config_file="/boot/firmware/config.txt" if [ ! -f "$config_file" ]; then config_file="/boot/config.txt" fi if [ ! -f "$config_file" ]; then print_error "Could not find config.txt file" exit 1 fi print_status "Using config file: $config_file" # Add additional UART configurations for multiple ports local uart_configs=( "dtoverlay=uart2" "dtoverlay=uart3" "dtoverlay=uart4" "dtoverlay=uart5" ) for config in "${uart_configs[@]}"; do if ! grep -q "^$config" "$config_file"; then echo "$config" | sudo tee -a "$config_file" print_success "Added: $config" else print_warning "$config already configured" fi done print_success "Hardware interfaces configuration completed" } # Setup sudo authorization setup_sudo_authorization() { print_step "Setting up sudo authorization..." SUDOERS_FILE="/etc/sudoers" # First, fix any existing syntax errors if sudo visudo -c 2>&1 | grep -q "syntax error"; then print_warning "Syntax error detected in sudoers file. Attempting to fix..." # Remove the problematic line if it exists sudo sed -i '/www-data ALL=(ALL) NOPASSWD: \/usr\/bin\/python3 \* www-data/d' "$SUDOERS_FILE" fi # Add proper sudo rules (each on a separate line) if ! sudo grep -q "/usr/bin/nmcli" "$SUDOERS_FILE"; then # Create a temporary file with the new rules cat < /dev/null # ModuleAir Pro 4G sudo rules ALL ALL=(ALL) NOPASSWD: /usr/bin/nmcli, /usr/sbin/reboot www-data ALL=(ALL) NOPASSWD: /usr/bin/git pull www-data ALL=(ALL) NOPASSWD: /usr/bin/ssh www-data ALL=(ALL) NOPASSWD: /usr/bin/python3 * www-data ALL=(ALL) NOPASSWD: /bin/systemctl * www-data ALL=(ALL) NOPASSWD: /var/www/moduleair_pro_4g/* EOF # Validate the temporary file if sudo visudo -c -f /tmp/sudoers_additions; then # Append to sudoers if valid using tee with sudo cat /tmp/sudoers_additions | sudo EDITOR='tee -a' visudo >/dev/null 2>&1 print_success "Sudo authorization added" else print_error "Failed to add sudo rules - syntax validation failed" sudo rm -f /tmp/sudoers_additions return 1 fi # Clean up sudo rm -f /tmp/sudoers_additions else print_warning "Sudo authorization already set. Skipping." fi # Validate sudoers file after changes if ! sudo visudo -c; then print_error "Sudoers file has syntax errors! Please fix manually with 'sudo visudo'" return 1 fi print_success "Sudo authorization configured successfully" } # Set device permissions set_permissions() { print_step "Setting device permissions..." # Set UART permissions sudo chmod 666 /dev/ttyAMA* 2>/dev/null || print_warning "Some UART devices may not be available yet" # Set I2C permissions sudo chmod 666 /dev/i2c-* 2>/dev/null || print_warning "I2C devices may not be available yet" # Add user to dialout group for serial access sudo usermod -a -G dialout "$USER" sudo usermod -a -G i2c "$USER" sudo usermod -a -G gpio "$USER" print_success "Device permissions set" } # Setup RTC save to DB service setup_rtc_service() { print_step "Setting up RTC save to DB service..." # Create the service file cat << 'EOF' | sudo tee /etc/systemd/system/rtc_save_to_db.service > /dev/null [Unit] Description=RTC Save to DB Script After=network.target [Service] ExecStart=/usr/bin/python3 /var/www/moduleair_pro_4g/RTC/save_to_db.py Restart=always RestartSec=1 User=root WorkingDirectory=/var/www/moduleair_pro_4g StandardOutput=append:/var/www/moduleair_pro_4g/logs/rtc_save_to_db.log StandardError=append:/var/www/moduleair_pro_4g/logs/rtc_save_to_db_errors.log [Install] WantedBy=multi-user.target EOF print_success "RTC service file created" # Create logs directory if it doesn't exist sudo mkdir -p /var/www/moduleair_pro_4g/logs sudo chown -R www-data:www-data /var/www/moduleair_pro_4g/logs print_status "Logs directory created/verified" # Reload systemd daemon print_status "Reloading systemd daemon..." sudo systemctl daemon-reload # Enable the service print_status "Enabling RTC service..." if sudo systemctl enable rtc_save_to_db.service; then print_success "RTC service enabled" else print_error "Failed to enable RTC service" return 1 fi # Start the service print_status "Starting RTC service..." if sudo systemctl start rtc_save_to_db.service; then print_success "RTC service started" else print_warning "Failed to start RTC service - may need reboot" fi # Check service status print_status "Checking RTC service status..." if systemctl is-active --quiet rtc_save_to_db.service; then print_success "RTC service is running" else print_warning "RTC service is not running - will start after reboot" fi return 0 } # Setup cron jobs setup_cron_jobs() { print_step "Setting up cron jobs..." if [ -f "/var/www/moduleair_pro_4g/cron_jobs" ]; then if sudo crontab "/var/www/moduleair_pro_4g/cron_jobs"; then print_success "Cron jobs set up successfully" else print_error "Failed to set up cron jobs" return 1 fi else print_warning "Cron jobs file not found. Skipping." fi } # Create databases create_databases() { print_step "Creating databases..." if [ -f "/var/www/moduleair_pro_4g/sqlite/create_db.py" ]; then if sudo /usr/bin/python3 "/var/www/moduleair_pro_4g/sqlite/create_db.py"; then print_success "Databases created successfully" else print_error "Failed to create databases" return 1 fi else print_warning "Database creation script not found" fi } # Fix database permissions fix_database_permissions() { print_step "Fixing database permissions..." # Set full permissions for sqlite directory if [ -d "/var/www/moduleair_pro_4g/sqlite" ]; then sudo chmod 777 /var/www/moduleair_pro_4g/sqlite/ print_success "Set permissions for sqlite directory" else print_warning "sqlite directory not found" fi # Set full permissions for sensors.db if [ -f "/var/www/moduleair_pro_4g/sqlite/sensors.db" ]; then sudo chmod 777 /var/www/moduleair_pro_4g/sqlite/sensors.db print_success "Set permissions for sensors.db" else print_warning "sensors.db not found" fi print_success "Database permissions fixed" } # Set configuration set_configuration() { print_step "Setting configuration..." if [ -f "/var/www/moduleair_pro_4g/sqlite/set_config_smart.py" ]; then if sudo /usr/bin/python3 "/var/www/moduleair_pro_4g/sqlite/set_config_smart.py"; then print_success "Configuration set successfully" else print_error "Failed to set configuration" return 1 fi else print_warning "Configuration script not found" fi } # Setup systemd services setup_services() { print_step "Setting up systemd services..." if [ -f "/var/www/moduleair_pro_4g/services/setup_services.sh" ]; then sudo chmod +x /var/www/moduleair_pro_4g/services/setup_services.sh if sudo /var/www/moduleair_pro_4g/services/setup_services.sh; then print_success "Systemd services configured" else print_error "Failed to setup systemd services" exit 1 fi else print_warning "setup_services.sh not found - services will need to be configured manually" fi } # Create startup script for permissions (runs at boot) create_boot_script() { print_step "Creating boot permission script..." cat << 'EOF' | sudo tee /usr/local/bin/moduleair-permissions.sh > /dev/null #!/bin/bash # ModuleAir Pro 4G Boot Permissions Script sleep 5 # Wait for devices to initialize chmod 666 /dev/ttyAMA* 2>/dev/null || true chmod 666 /dev/i2c-* 2>/dev/null || true EOF sudo chmod +x /usr/local/bin/moduleair-permissions.sh # Create systemd service for boot permissions cat << 'EOF' | sudo tee /etc/systemd/system/moduleair-permissions.service > /dev/null [Unit] Description=ModuleAir Pro 4G Device Permissions After=multi-user.target [Service] Type=oneshot ExecStart=/usr/local/bin/moduleair-permissions.sh RemainAfterExit=yes [Install] WantedBy=multi-user.target EOF sudo systemctl enable moduleair-permissions.service print_success "Boot permission script created and enabled" } # Final status check final_status() { print_header "INSTALLATION COMPLETE" print_step "Checking service status..." # Check Apache if systemctl is-active --quiet apache2; then print_success "Apache2 is running" else print_warning "Apache2 is not running" fi # Check for ModuleAir services local services=( "moduleair-npm-data.timer" "moduleair-co2-data.timer" "moduleair-sara-data.timer" "moduleair-bme-data.timer" "moduleair-boot.service" "rtc_save_to_db.service" ) for service in "${services[@]}"; do if systemctl is-enabled --quiet "$service" 2>/dev/null; then print_success "Service enabled: $service" else print_warning "Service not found: $service" fi done print_header "INSTALLATION SUMMARY" echo -e "${GREEN}✓${NC} System packages installed" echo -e "${GREEN}✓${NC} Python packages installed" echo -e "${GREEN}✓${NC} Repository cloned" echo -e "${GREEN}✓${NC} Directory structure created" echo -e "${GREEN}✓${NC} Apache web server configured" echo -e "${GREEN}✓${NC} Hardware interfaces enabled" echo -e "${GREEN}✓${NC} System optimizations applied" echo -e "${GREEN}✓${NC} Device permissions set" echo -e "${GREEN}✓${NC} Sudo authorization configured" echo -e "${GREEN}✓${NC} Databases created" echo -e "${GREEN}✓${NC} Configuration set" echo -e "${GREEN}✓${NC} Cron jobs configured" echo -e "${GREEN}✓${NC} Matrix binaries verified" echo -e "${GREEN}✓${NC} Systemd services configured" echo -e "${GREEN}✓${NC} RTC save to DB service installed" echo -e "${GREEN}✓${NC} Boot scripts created" print_header "NEXT STEPS" echo -e "${YELLOW}1.${NC} ${RED}REBOOT REQUIRED${NC} - Hardware interfaces need reboot to activate:" echo -e " ${CYAN}sudo reboot${NC}" echo -e "" echo -e "${YELLOW}2.${NC} After reboot, verify I2C and UART devices:" echo -e " ${CYAN}ls -la /dev/i2c-* /dev/ttyAMA*${NC}" echo -e "" echo -e "${YELLOW}3.${NC} Check service status:" echo -e " ${CYAN}systemctl list-timers | grep moduleair${NC}" echo -e " ${CYAN}systemctl status rtc_save_to_db.service${NC}" echo -e "" echo -e "${YELLOW}4.${NC} Test RTC module (after reboot):" echo -e " ${CYAN}python3 /var/www/moduleair_pro_4g/RTC/read.py${NC}" echo -e "" echo -e "${YELLOW}5.${NC} Access web interface at:" echo -e " ${CYAN}http://$(hostname -I | awk '{print $1}')${NC}" echo -e "" echo -e "${YELLOW}6.${NC} Check logs if needed:" echo -e " ${CYAN}tail -f /var/www/moduleair_pro_4g/logs/*.log${NC}" echo -e " ${CYAN}tail -f /var/www/moduleair_pro_4g/logs/rtc_save_to_db.log${NC}" echo -e "" echo -e "${YELLOW}7.${NC} Check cron jobs:" echo -e " ${CYAN}sudo crontab -l${NC}" echo -e "" echo -e "${YELLOW}8.${NC} Verify audio is disabled (after reboot):" echo -e " ${CYAN}lsmod | grep snd_bcm2835${NC} (should return nothing)" echo -e "" echo -e "${YELLOW}9.${NC} Verify CPU isolation (after reboot):" echo -e " ${CYAN}cat /proc/cmdline | grep isolcpus${NC}" echo -e "" print_warning "I2C and UART devices will NOT work until after reboot!" print_warning "Audio and CPU isolation changes require reboot to take effect!" } # Main installation function main() { print_header "MODULEAIR PRO 4G INSTALLATION" print_status "Starting automated installation..." # Set non-interactive mode globally export DEBIAN_FRONTEND=noninteractive export NEEDRESTART_MODE=a check_root check_sudo pre_configure_packages update_system install_dependencies install_python_packages clone_repository configure_apache enable_hardware system_optimizations set_permissions setup_sudo_authorization # Database and configuration setup create_databases set_configuration fix_database_permissions # Fix permissions after database creation setup_cron_jobs setup_services setup_rtc_service # New function for RTC service create_boot_script final_status print_header "INSTALLATION COMPLETED SUCCESSFULLY" print_success "ModuleAir Pro 4G has been installed successfully!" print_warning "Please reboot the system to complete the installation." } # Run main function main "$@"