enum MabniAIAction { //% block="stop" Stop = 1, //% block="forward" Forward = 2, //% block="turn left" Left = 3, //% block="turn right" Right = 4 } /** Local AI results from Mabni AI Maker. Image inference runs on the computer. */ //% color="#8463C4" icon="\uf0eb" weight=87 block="AI Maker" namespace mabniAI { let started = false; let action = MabniAIAction.Stop; let motorPermission = false; let lastMessage = 0; let sensorStreaming = false; const eventSource = 14567; function update(next: MabniAIAction): void { if (action !== next) { action = next; control.raiseEvent(eventSource, next); } } /** Start the local USB bridge. Silence or lost communication requests a stop. */ //% blockId=mabni_ai_start block="start offline AI USB bridge" //% weight=100 export function start(): void { if (started) return; started = true; serial.redirectToUSB(); serial.setBaudRate(BaudRate.BaudRate115200); serial.onDataReceived(serial.delimiters(Delimiters.NewLine), function () { let line = serial.readUntil(serial.delimiters(Delimiters.NewLine)).trim(); lastMessage = control.millis(); if (line === "HELLO") { motorPermission = false; update(MabniAIAction.Stop); serial.writeLine("READY:MABNI-AI:1"); } else if (line === "ARM") { update(MabniAIAction.Stop); motorPermission = true; } else if (line === "DISARM") { motorPermission = false; update(MabniAIAction.Stop); } else if (line === "SENSORON") { sensorStreaming = true; } else if (line === "SENSOROFF") { sensorStreaming = false; } else { update(line === "F" ? MabniAIAction.Forward : line === "L" ? MabniAIAction.Left : line === "R" ? MabniAIAction.Right : MabniAIAction.Stop); } }); basic.forever(function () { if (control.millis() - lastMessage > 1100) update(MabniAIAction.Stop); if (sensorStreaming) serial.writeLine("D," + input.acceleration(Dimension.X) + "," + input.acceleration(Dimension.Y) + "," + input.acceleration(Dimension.Z)); basic.pause(100); }); } /** Run blocks when the locally trained model changes its action. */ //% blockId=mabni_ai_on_action block="on AI %which" //% weight=90 export function onAction(which: MabniAIAction, handler: () => void): void { control.onEvent(eventSource, which, handler); } /** Motor signals stay disabled until the adult wiring check is enabled in AI Maker. */ //% blockId=mabni_ai_motor_permission block="AI motor outputs enabled" //% weight=30 export function motorsEnabled(): boolean { return motorPermission; } /** Read the latest action. An uncertain prediction sends stop. */ //% blockId=mabni_ai_current block="current AI action" //% weight=20 export function currentAction(): MabniAIAction { return action; } }