Программа для Космического корабля (детский симулятор)

Здравствуйте, уважаемые профессионалы!

Требуется написать программу для космического корабля (детский симулятор). Космический корабль состоит из кабины и стола управления. В кабине и в столе установлены по Arduino Mega 2560. Общаются Arduino друг с другом по протоколу I2C (в таблице связей указаны PIN по которому они подключены друг к другу).

В кабине к Arduino, через блоки реле подключено по группам светодиодное освещение кабины и несколько отдельных лампочек.

В столе к Arduino подключены кнопки (около 70 шт.), переключатели и несколько лампочек. При нажатии на кнопки должны включаться определенные PIN в кабине. В таблице связей указано, что должно происходить при нажатии на кнопки (вышлю исполнителю на емайл).

Должно быть реализовано 2 режима работы: 1 – работа в режиме «1»; 2 – работа в режиме «Миссия – 1».

Вот кратко в этом смысл.

Чтобы визуально понимать о чем идет речь, можно ознакомиться с этапом постройки на моем канале по ссылке: Космический корабль своими руками (A Hande-made spaceship) Часть 1 - YouTube

На данный момент корабль собран, осталось написать программу.

Все нюансы буду обсуждать индивидуально.

Буду рад сотрудничать, пишите мне на емайл: sergey-86@yandex.ru

Дополнительная информация:

Активное оборудование:

Arduino Mega 2560 – 2 шт.

DF MP3 player mini – 2 шт.

Клавиатура Arduino – 2 шт.

Что должно работать в режиме «1»

  • Все кнопки. Нажимаем кнопку, издается запрограммированный звук этой кнопки, загораются светодиоды в Кабине (см. вкладку «стол-кабина»);

  • Должна работать возможность включения музыки с dfmp3player (MP3 DF PLAYER2 в таблице подключений на вкладке Arduino_1_Стол). Управление MP3 DF PLAYER2 должно осуществлять с кнопок клавиатуры Arduino (pin A1) –кнопки: «вкл/откл», «следующая», «предыдущая», «звук тише», «звук громче».

Что должно работать в режиме «Миссия - 1»

  • Согласно циклограмме, указанной во вкладке «Миссия – 1» (вышлю исполнителю на емайл)

НЮАНСЫ выявленные входе монтажа:

  • Есть кнопки с фиксацией, есть без фиксации (указано в столбце «кнопка с фиксацией». Соответственно необходимо реализовать в коде работу кнопок по импульсу).
  • Опытным путем было установлено, что библиотека «DFRobotdfplayerMini» не хочет нормально работать с Arduino Mega 2560. А именно, происходит единичный запуск звука и далее код не выполняется. Тот же код был залит на Arduino UNO и код выполнялся. С библиотекой «DFPlayermini_fast» код на Arduino Mega 2560 выполняется, НО в этой библиотеке нет функции отключения усилителя, а это ВАЖНО, так как в схеме уже стоит усилитель звука.

Скорее всего цена запляшет от $500

я был бы более осторожен. от 200000р

Согласен с 200к. Тут ещё и сроки будут длинные.

Так это же не настоящий корабль…
))

тем более автоматика вся сделана, осталось скетчик набросать )))

ну так и рубль не настоящий, а деревянный, российский.

В принципе с этим согласен. Вчера посмотрел цены на автомобили. Новые и б.у, но свежие, 3-4 года.
Только жигули. Другое ниасилю. Бэк ту юсса.

1 лайк

ты не одинок )))

Напишу за 190 тр.

Кроме шуток, подскажите где можно наколупать математику инерции.

Пишу кукурузник для unreal engine 1, сейчас работает втупую (абсолютным переназначением координат вектора location).

Переписывал под накопление длины вектора velocity установив физику PHYS_Falling (самая тщательно рассчитываемая и реплицируемая по сети физика) - вроде подход правильный, но на выходе получается кал гавна.

Сейчас имею на выходе нечто типа VTOL как у Шварценеггера в фильме “Правдивая ложь” (горизонтально взлетает на скорости больше 150 uu, вертикальная/горизонтальная автопосадка на скорости меньше 90. чтобы имитировать потерю подъёмной силы, недостаток скорости вносит немного “проваливания”, то есть уменьшения location.z)

Есьчо подсказать?

class asw extends asmd;
#exec texture import file="textures\ttagbarhl.png" name="ttagbarhl" package="aerscope" group="None" mips=1 flags=0 btc=-2
#exec texture import file="textures\ttagbarhs.png" name="ttagbarhs" package="aerscope" group="None" mips=1 flags=0 btc=-2
#exec texture import file="textures\ttagbarvl.png" name="ttagbarvl" package="aerscope" group="None" mips=1 flags=0 btc=-2
#exec texture import file="textures\ttagbarvs.png" name="ttagbarvs" package="aerscope" group="None" mips=1 flags=0 btc=-2
#exec texture import file="textures\ttagpixel.png" name="ttagpixel" package="aerscope" group="None" mips=1 flags=0 btc=-2

#exec audio import file="sounds\ttagengend3.wav"  name="ttagengend"  package="aerscope" group="Sound"
#exec audio import file="sounds\ttagengloop3.wav" name="ttagengloop" package="aerscope" group="Sound"
#exec audio import file="sounds\ttagengsta3.wav"  name="ttagengsta"  package="aerscope" group="Sound"

var aswcam fpv;
var bool deployed;
var rotator freeze_rot;
var vector  freeze_loc;
var float movespeed, actualspeed, movespeedtimer, movemeasspeedtimer, movesndstarttimer, altitude;
var float   scale1, scale128, clipxdiv2, clipydiv2;
var bool    canvas_set,ok_land,ok_takeoff;

function fire(float f){}
function altfire(float f){}

function calccoords(canvas c, playerpawn p){
        if(canvas_set) return;
        scale1 = c.clipx / 640;
        scale128 = scale1 * 128;
        clipxdiv2 = c.clipx / 2;
        clipydiv2 = c.clipy / 2;
        canvas_set = true;
}

function postrender(canvas c){
        local playerpawn p;
        local pawn t;
        local int i,j,k,l,m,xp,yp;
        local vector dist,dist_c,dir,x,y,z,dir_bounds[8];
        local float d,ms_s,bearing,bear_s,alt_s,dirdotx,fovscale;
        local string s;
        if(!deployed || fpv == none) return;
        p = playerpawn(owner);
        if(p == none) return;
        calccoords(c,p);

        dist = fpv.location - p.location;
        dist_c = dist;
        alt_s = altitude;
        alt_s = alt_s % 80;
        ms_s = movespeed;
        ms_s = ms_s % 80;
        bearing = fpv.rotation.yaw;
        bearing = bearing % 65536;
        bearing /= 182.0;
        if(bearing < 0) bearing += 360;
        bear_s = bearing % 80;
        c.drawcolor = makecolor(255,255,255);
   // ====== ABR ==========================================================================================================
        if(p.bIsCrouching){ c.setpos(250,c.clipy-40); c.drawtext("[ABR]"); }
   // ====== RSSI =========================================================================================================
        s="";
        j=clamp(int(vsize(dist)/256),0,10);
        for(i=1;i<10-j;i++) s $= "|";
        c.setpos(50,c.clipy-40); c.drawtext("RSSI: "$s);
   // ====== altimeter ====================================================================================================
        yp = 0 - int(alt_s);
        c.setpos(40,clipydiv2-5); c.drawtext(int(altitude * 2.125));
        for(i=0;i<115;i++){
           if(i%10 == 0){ c.setpos(100,yp); if(yp > 60 && yp < c.clipy-60) c.drawicon(texture'ttagbarhl', 1);
                   }else{ c.setpos(108,yp); if(yp > 60 && yp < c.clipy-60) c.drawicon(texture'ttagbarhs', 1); }
           yp += 8;
        }
   // ====== speedmeter ===================================================================================================
        yp = 0 - int(ms_s);
        c.setpos(c.clipx-70,clipydiv2-5); c.drawtext(int(movespeed * 1.619));
        for(i=0;i<115;i++){
           if(i%10 == 0){ c.setpos(c.clipx-100,yp); if(yp > 60 && yp < c.clipy-60) c.drawicon(texture'ttagbarhl', 1);
                   }else{ c.setpos(c.clipx-100,yp); if(yp > 60 && yp < c.clipy-60) c.drawicon(texture'ttagbarhs', 1); }
           yp += 8;
        }
   // ====== compass ======================================================================================================
        xp = 0 - int(bear_s);
        c.setpos(clipxdiv2-20,30); c.drawtext(int(bearing));
        for(i=0;i<115;i++){
           c.setpos(xp,54); if(xp > 140 && xp < c.clipx-124) c.drawicon(texture'ttagbarvs', 1);
           xp += 16;
        }
   // ====== targets display ==============================================================================================
        getaxes(fpv.rotation,x,y,z);
        FovScale = 1 / Tan(FClamp(p.DesiredFOV, 1, 170) / 360 * Pi);
        c.style = ERenderStyle.STY_Translucent;
        foreach RadiusActors(class'pawn', t, 12000, fpv.location){
            if(!validate_target(t)) continue;
            Dir = fpv.location - t.location;
            if(VSize(Dir) > 2000 && t.group != 'AERtarget') continue;
            if ( ((Dir / VSize(Dir)) Dot X) > 0.7 * FovScale) continue;
            for(i=0;i<8;i++) dir_bounds[i] = dir;
            d = t.collisionheight/2;
            for(i=0;i<4;i++) dir_bounds[i].z -= d;
            for(i=4;i<8;i++) dir_bounds[i].z += d;
            d = t.collisionradius/2;
            dir_bounds[0].y -= d; dir_bounds[1].y -= d; dir_bounds[4].y -= d; dir_bounds[5].y -= d;
            dir_bounds[2].y += d; dir_bounds[3].y += d; dir_bounds[6].y += d; dir_bounds[7].y += d;
            dir_bounds[0].x -= d; dir_bounds[3].x -= d; dir_bounds[4].x -= d; dir_bounds[7].x -= d;
            dir_bounds[1].x += d; dir_bounds[2].x += d; dir_bounds[5].x += d; dir_bounds[6].x += d;
            dirdotx = dir dot x;
            c.drawcolor = t.group == 'AERtarget' ? makecolor(200,200,255) : makecolor(32,32,32);
            for(i=0;i<8;i++){
                dir_bounds[i] /= dirdotx;
                XP = 0.5 * (c.SizeX + c.SizeX * (dir_bounds[i] Dot Y) * FovScale);
                YP = 0.5 * (c.SizeY - c.SizeX * (dir_bounds[i] Dot Z) * FovScale);
                c.SetPos(XP - 0.5 * 4 * scale1, YP - 0.5 * 4 * scale1);
                c.DrawIcon(texture'ttagpixel', scale1/4);
            }
       }
       c.Reset();
       c.drawcolor = makecolor(255,255,255);
}

function tick(float f){
        local playerpawn p;
        local vector l,x,y,z,hl,hn;
        local actor atarg;
        local pawn ptarg;
        p = playerpawn(owner);
        if(p == none) return;
        if(p.weapon != self) return;
        if(deployed && fpv != none){
            l = fpv.location;
            getaxes(fpv.rotation,x,y,z);
            atarg = trace(hl,hn,l + 2000*x,l,true);
        }
        if(atarg != none && atarg.bIsPawn) ptarg = pawn(atarg);
        if(ptarg != none) attempt_tag_target(ptarg);
        if(p.bFire    != 0 && !deployed) deploy_fpv(p);
        if(p.bAltFire != 0 &&  deployed) collapse_fpv();
        if(!deployed || fpv == none) return;
        rotate_fpv(p);
        thrust_fpv(p,f);
        move_fpv(p,f);
        fpv_speed(f);
        move_sound(f);
}

function bool validate_target(pawn targ){
        if(targ.group == 'AERtarget') return true;
        if(targ==owner) return false;
        if(targ.health <= 0){
            targ.group = '';
            return false;
        }
        if(bool(flockpawn(targ)) || bool(flockmasterpawn(targ)) || bool(nali(targ)) || bool(cow(targ))) return false;
        if(targ.isa('stationarypawn') || targ.isa('cspawn') || targ.isa('upakintermissioncam')) return false;
        return true;
}
function attempt_tag_target(pawn targ){
        if(validate_target(targ) && targ.AttitudeToPlayer<ATTITUDE_IGNORE) targ.group = 'AERtarget';
}

function rotate_fpv(playerpawn p){
        fpv.setrotation(p.viewrotation);
        p.setrotation(freeze_rot);
}
function bool thrust_val(){
        local vector l,x,y,z,hl,hn;
        local bool ok_fwd,ok_top,ok_bot,ok_l,ok_r;
        l = fpv.location;
        getaxes(fpv.rotation,x,y,z);
        trace(hl,hn,l + 64*x,l,true); ok_fwd = vsize(hl-l) > 40;
//      trace(hl,hn,l - 64*x,l,true); ok_bak = vsize(hl-l) > 8;  // never used
        trace(hl,hn,l + 64*y,l,true); ok_r   = vsize(hl-l) > 8;
        trace(hl,hn,l - 64*y,l,true); ok_l   = vsize(hl-l) > 8;
        trace(hl,hn,l + 64*z,l,true); ok_top = vsize(hl-l) > 8;
        trace(hl,hn,l - 64*z,l,true); ok_bot = vsize(hl-l) > 8;
        trace(hl,hn,l - vect(0,0,10000),l,true); altitude = vsize(hl-l);
        return (ok_fwd && ok_r && ok_l && ok_top && ok_top);
}
function thrust_fpv(playerpawn p,float f){
        local bool tval,ok_autobrk;
        local int shm;
        local float min_thr;
        tval = thrust_val();
        shm = !p.bIsCrouching ? 1 : 5;
        if(!ok_land && movespeed<160 && altitude<32) ok_land = true;
        if(ok_land && movespeed>160) ok_land = false;
        min_thr = ok_land ? 0 : 30; //was 186,180,230

        if(movesndstarttimer>0.1 && movespeed<170) movespeed += 1; // auto launch thrust
        movespeedtimer -= f;
        if(movespeedtimer > 0) return;
        movespeedtimer = !p.bIsCrouching ? 0.1 : 0.02;
        if(ok_land) movespeedtimer = 0.02;
        if(movespeed <= 2 && ok_land && !ok_takeoff){
           p.clientmessage("ok_takeoff.",'pickup');
           fpv.setphysics(PHYS_Falling);
           movespeed = 0;
           ok_takeoff = true;
           fpv.ambientsound = none;
           fpv.playsound(sound'ttagengend', SLOT_None, 16);
        // spawn pickup here
        // collapse_fpv();
           return;
        }
        ok_autobrk = ok_land && movespeed<150 && p.bIsCrouching;
        if(!p.bWasForward || !tval){
           if(!p.bWasBack && p.bIsCrouching) shm = 1;
           if(ok_autobrk && movespeed > min_thr) movespeed -= 1*shm;  // auto fly/landing brake
           if(p.bWasBack  && movespeed > min_thr) movespeed -= 2*shm;
           if(movespeed < 0) movespeed = 0;
           return;
        }
        if(tval && movespeed < 900) movespeed += 1*shm;
}
function move_fpv(playerpawn p,float f){
        local vector l,x,y,z;
        local int lackspeed;
        p.setlocation(freeze_loc);
        if(!thrust_val()) return;
        l = fpv.location;
        getaxes(fpv.rotation,x,y,z);
        if(movespeed > 0) l += movespeed*x*f;
        if(movesndstarttimer<=0 && altitude>12 && movespeed<150){
          lackspeed = 150 - movespeed;
          if(lackspeed>100) lackspeed -= 100;
          if(movespeed < 50) lackspeed += lackspeed;
          if(movespeed < 25) lackspeed *= 4;
          l.z -= lackspeed*1.2*f;
        }
        fpv.setlocation(l);
}
function fpv_speed(float f){
        movemeasspeedtimer -= f;
        if(movemeasspeedtimer > 0) return;
        movemeasspeedtimer = 0.4;
        actualspeed = int(vsize(fpv.spd_oldloc-fpv.location));
        fpv.spd_oldloc = fpv.location;
}

function move_sound(float f){
        if(movesndstarttimer > 0){
            movesndstarttimer -= f;
            return;
        }
        if(movespeed >= 50)   fpv.soundpitch = 64 + int(movespeed/100);
        fpv.ambientsound = sound'ttagengloop';
}

function deploy_fpv(playerpawn p){
        local vector x,y,z;
        if(deployed) return;
        getaxes(p.viewrotation,x,y,z);
        movespeed = 50;
        fpv = spawn(class'aswcam',,,p.location + 40*x + 10*z,p.viewrotation);
        if(fpv==none) return;
        deployed = true;
        fpv.w = self;
        freeze_rot = p.viewrotation;
        freeze_loc = p.location;
        movesndstarttimer = 1.02;
        p.viewtarget = fpv;
        fpv.playsound(sound'ttagengsta', SLOT_None, 16);
        p.dodgeclicktime = 0;
}
function collapse_fpv(){
        local playerpawn p;
        if(!deployed || fpv == none) return;
        p = playerpawn(owner);
        if(p == none) return;
        if(p.viewtarget == fpv) p.viewtarget = none;
        p.viewrotation = freeze_rot;
        p.dodgeclicktime = p.default.dodgeclicktime;
//        fpv.destroy(); //todo settimeout
        fpv.mesh = lodmesh'unrealshare.rabbit';
        fpv.drawtype = dt_mesh;
        fpv.bhidden = false;
        fpv = none;
        deployed = false;
}
function tweendown(){
        local playerpawn p;
        p = playerpawn(owner);
        if(p == none) return;
        super.tweendown();
        if(!deployed) return;
        bringup();
        p.weapon = self;
}

function timer(){
        local playerpawn p;
        p = playerpawn(owner);
        if(p == None){
                GotoState('Pickup');
                return;
        }
        if(!isinstate('Idle')) GotoState('Idle');
        if(p.weapon != self) return;
        settimer(0.05, true);
}

defaultproperties{
  movespeed=50
  ok_takeoff=false
  ok_land=false
  fpv=none
  deployed=false
  canvas_set=false
}

Самое интересное с точки зрения движения живёт в функциях tick(), move_fpv()

Текущий вариант использует физику PHYS_None

Учебник физики + учебник матанализа + учебник диффуров.
Если инерция предполагается в пространстве с более чем одним измерением, то еще учебник по аналитической геометрии.

1 лайк

На правах собиравшего похожее и взимодействовавшего Заметить, что Не учтёная половинга работа - Написать сценарии взаимодействия. Что в какой промежуток времени надо включить, как просигнализировать о Некорректном выполнении и т.д.
Обычно это тоже вписывают или в прошивку или как сценарий симуляций на компьютере к которому и подключены ардуины с индикаторами и кнопками.
Из того что вижу уже просящимся под улучшение - вместо планшета - просто старенький монитор от стпятаного в нутрь ноута или микро атх Атома - он и видео вне миссий сможет удобнее показывать через плейлист и в Симуляции будет полезен как подсказчик, надписи и сообщения выводить
и как сервер симуляций можно использовать и половину кода-логики (симуляций\ситуаций) снять с прошивки и пренести на тот же пайтон - с визуализацией на экране - как вашей душе будет удобно.
Сейчас же он оттягивает на себя внимание и будет отвлекать во время симуляций взлёта посадки-вместо помощи.
У вас так же видны разёмы Джек 5мм - они не задействованы и не описаны в ТЗ - если это украшение - то спорное. Если будут как часть миссии по перекомутация на высоте - очень не удобна. А по играм типа амонг Ас дети любят играи=ть в правильное подключениее проводов.
Я бы рекомендовал под панелбю, где у вас пустая панель сделать щит для перекомутаций.
И задейстовать все возможности позабавить детей.

Почему я счёл половиной работы сценарии - потому что с опытом-без опыта занимает не мало времени не только взять ситуацию и разложить её на действия и последствия, но и ещё подобрать Тайминги - временные промежутки в которые ДЕТИ должны уложиться с решением задачи.
А это Работа и Работа. Работа придумать и отладить и Работа для детей - пройти миссию.
Дети не любят Работать, а если миссия слишком простая - будет заброшен весь симулятор.
По моим наблюдениям даже сложные мисси хорошо заходят при соревновательном подходе, когда детей Одного возраста Несколько и они как подбадривают друг-друга, так и подзуживают.
И с таким подходом мы получаем не домашнюю игрушку, а инструмент для работы - детского досуга.

Потому соглашусь, что такое не делается быстро, а значит и не дёшево. Но вполне собираемо.