├── .DS_Store ├── .gitignore ├── .header.json ├── .vscode ├── settings.json ├── tasks.json └── tasks.json.old ├── LICENSE ├── Makefile ├── README.md ├── _locales ├── .DS_Store ├── zh-cn │ └── robotbit-strings.json └── zh-tw │ └── robotbit-strings.json ├── icon.png ├── icon2.png ├── main.ts ├── pxt.json ├── test.ts └── tsconfig.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KittenBot/pxt-robotbit/ca5a3606c94b2e35b8d237fd96d1d6aae8093127/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | built 2 | node_modules 3 | yotta_modules 4 | yotta_targets 5 | pxt_modules 6 | *.db 7 | *.tgz 8 | .DS_store 9 | */.DS_store 10 | -------------------------------------------------------------------------------- /.header.json: -------------------------------------------------------------------------------- 1 | { 2 | "target": "microbit", 3 | "targetVersion": "2.3.70", 4 | "name": "robotbit", 5 | "meta": {}, 6 | "editor": "tsprj", 7 | "pubId": "", 8 | "pubCurrent": false, 9 | "_rev": null, 10 | "id": "5f3fc131-dd08-4e8a-2a3f-61c22ba89542", 11 | "recentUse": 1657684445, 12 | "modificationTime": 1657684445, 13 | "blobId": null, 14 | "blobVersion": null, 15 | "blobCurrent": false, 16 | "isDeleted": false, 17 | "path": "pxt-robotbit", 18 | "githubCurrent": false, 19 | "saveId": null 20 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnType": true, 3 | "files.autoSave": "afterDelay", 4 | "files.watcherExclude": { 5 | "**/.git/objects/**": true, 6 | "**/built/**": true, 7 | "**/node_modules/**": true, 8 | "**/yotta_modules/**": true, 9 | "**/yotta_targets": true, 10 | "**/pxt_modules/**": true 11 | }, 12 | "search.exclude": { 13 | "**/built": true, 14 | "**/node_modules": true, 15 | "**/yotta_modules": true, 16 | "**/yotta_targets": true, 17 | "**/pxt_modules": true 18 | } 19 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | 2 | // A task runner that calls the PXT compiler and 3 | { 4 | "version": "2.0.0", 5 | 6 | // The command is pxt. Assumes that PXT has been installed using npm install -g pxt 7 | "command": "pxt", 8 | 9 | "tasks": [ 10 | { 11 | "label": "deploy", 12 | "type": "shell", 13 | "command": "pxt", 14 | "args": [ 15 | "deploy", 16 | "" 17 | ], 18 | "problemMatcher": "$tsc", 19 | "group": { 20 | "_id": "build", 21 | "isDefault": false 22 | } 23 | }, 24 | { 25 | "label": "build", 26 | "type": "shell", 27 | "command": "pxt", 28 | "args": [ 29 | "build", 30 | "" 31 | ], 32 | "problemMatcher": "$tsc", 33 | "group": { 34 | "_id": "test", 35 | "isDefault": false 36 | } 37 | }, 38 | { 39 | "label": "clean", 40 | "type": "shell", 41 | "command": "pxt", 42 | "args": [ 43 | "clean", 44 | "" 45 | ], 46 | "problemMatcher": "$tsc", 47 | "group": { 48 | "_id": "test", 49 | "isDefault": false 50 | } 51 | }, 52 | { 53 | "label": "serial", 54 | "type": "shell", 55 | "command": "pxt", 56 | "args": [ 57 | "serial", 58 | "" 59 | ], 60 | "problemMatcher": "$tsc", 61 | "group": { 62 | "_id": "test", 63 | "isDefault": false 64 | } 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /.vscode/tasks.json.old: -------------------------------------------------------------------------------- 1 | 2 | // A task runner that calls the PXT compiler and 3 | { 4 | "version": "0.1.0", 5 | 6 | // The command is pxt. Assumes that PXT has been installed using npm install -g pxt 7 | "command": "pxt", 8 | 9 | // The command is a shell script 10 | "isShellCommand": true, 11 | 12 | // Show the output window always. 13 | "showOutput": "always", 14 | 15 | "tasks": [{ 16 | "taskName": "deploy", 17 | "isBuildCommand": true, 18 | "problemMatcher": "$tsc", 19 | "args": [""] 20 | }, { 21 | "taskName": "build", 22 | "isTestCommand": true, 23 | "problemMatcher": "$tsc", 24 | "args": [""] 25 | }, { 26 | "taskName": "clean", 27 | "isTestCommand": true, 28 | "problemMatcher": "$tsc", 29 | "args": [""] 30 | }, { 31 | "taskName": "serial", 32 | "isTestCommand": true, 33 | "problemMatcher": "$tsc", 34 | "args": [""] 35 | }] 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Riven 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: deploy 2 | 3 | build: 4 | pxt build 5 | 6 | deploy: 7 | pxt deploy 8 | 9 | test: 10 | pxt test 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # robotbit 2 | 3 | Extension for Kittenbot Robotbit 4 | 5 | ## Feature 6 | 7 | - Designed for robotic projects over microbit 8 | - On board battery source 9 | - Drive 8x servos and 4x DC motors and the same time (with 3.7v battery source to VM) 10 | - Drive 2x Micro Stepper Motors 11 | - On board buzzer 12 | - On board 4x RGB Neo Pixels 13 | 14 | # Blocks Preview 15 | ![image](https://user-images.githubusercontent.com/3390845/34512193-b3e0ffdc-f09b-11e7-839f-0c9c3563ac94.png) 16 | 17 | # Hardware Preview 18 | ## Front 19 | ![image](https://user-images.githubusercontent.com/3390845/34511999-4b3fdef4-f09a-11e7-960e-5661268b0ba7.png) 20 | 21 | ## Back 22 | ![image](https://user-images.githubusercontent.com/3390845/34512098-04fecc1a-f09b-11e7-9ec6-62fcc0780773.png) 23 | 24 | 25 | ---------- 26 | 27 | For more infomation please visit [http://kittenbot.cc/bbs/](http://kittenbot.cc/bbs/ "Kittenbot BBS") 28 | 29 | ## License 30 | 31 | MIT 32 | 33 | ## Supported targets 34 | 35 | * for PXT/microbit 36 | (The metadata above is needed for package search.) 37 | 38 | ```package 39 | robotbit=github:Kittenbot/pxt-robotbit 40 | ``` -------------------------------------------------------------------------------- /_locales/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KittenBot/pxt-robotbit/ca5a3606c94b2e35b8d237fd96d1d6aae8093127/_locales/.DS_Store -------------------------------------------------------------------------------- /_locales/zh-cn/robotbit-strings.json: -------------------------------------------------------------------------------- 1 | { 2 | "robotbit.rgb|block": "板载RGB", 3 | "robotbit.Servo|block": "9g舵机|%index|角度 %degree", 4 | "robotbit.GeekServo|block": "Geekservo|9g舵机|%index|角度 %degree", 5 | "robotbit.GeekServo2KG|block": "Geekservo|2KG舵机|%index|角度 %degree", 6 | "robotbit.GeekServo5KG|block": "Geekservo|5KG舵机|%index|角度 %degree", 7 | "robotbit.GeekServo5KG_Motor|block": "Geekservo|5KG舵机_电机|%index|速度 %speed", 8 | "robotbit.StepperDegree|block": "步进 28BYJ-48|%index|角度 %degree", 9 | "robotbit.StepperTurn|block": "步进 28BYJ-48|%index|圈数 %turn", 10 | "robotbit.StepperDual|block": "双步进(角度) |M1 %degree1| M2 %degree2", 11 | "robotbit.StpCarMove|block": "步进小车 前进 |距离(cm) %distance|轮直径(mm) %diameter", 12 | "robotbit.StpCarTurn|block": "步进小车 转向 |角度 %turn|轮直径(mm) %diameter|轮距(mm) %track", 13 | "robotbit.MotorRun|block": "电机|%index|速度 %speed", 14 | "robotbit.MotorRunDual|block": "电机|%motor1|速度 %speed1|%motor2|速度 %speed2", 15 | "robotbit.MotorRunDelay|block": "电机|%index|速度 %speed|延时 %delay|s", 16 | "robotbit.MotorStop|block": "电机 停止|%index|", 17 | "robotbit.MotorStopAll|block": "停止所有电机", 18 | "robotbit.RgbUltrasonic|block": "猫耳RGB超声波|引脚 %pin", 19 | "robotbit.HoleUltrasonic|block": "猫耳洞超声波|引脚 %pin|单位 %unit", 20 | "robotbit.MatrixDraw|block": "LED矩阵 描点|X %x|Y %y", 21 | "robotbit.MatrixClear|block": "清空 LED矩阵 ", 22 | "robotbit.MatrixRefresh|block": "刷新 LED矩阵 ", 23 | 24 | "robotbit.ValueUnit.Millimeter|block": "毫米", 25 | "robotbit.ValueUnit.Centimeters|block": "厘米", 26 | 27 | "{id:group}Servo": "舵机", 28 | "{id:group}Motor": "电机/步进电机", 29 | "{id:group}Modules": "外接模块" 30 | } 31 | -------------------------------------------------------------------------------- /_locales/zh-tw/robotbit-strings.json: -------------------------------------------------------------------------------- 1 | { 2 | "robotbit.Servo|block": "9g舵機|%index|角度 %degree", 3 | "robotbit.GeekServo|block": "Geekservo|9g舵機|%index|角度 %degree", 4 | "robotbit.GeekServo2KG|block": "Geekservo|2KG舵機|%index|角度 %degree", 5 | "robotbit.GeekServo5KG|block": "Geekservo|5KG舵機|%index|角度 %degree", 6 | "robotbit.GeekServo5KG_Motor|block": "Geekservo|5KG舵機_馬達|%index|速度 %speed", 7 | "robotbit.StepperDegree|block": "步進 28BYJ-48|%index|角度 %degree", 8 | "robotbit.StepperTurn|block": "步進 28BYJ-48|%index|圈數 %turn", 9 | "robotbit.StepperDual|block": "雙步進(角度) |M1 %degree1| M2 %degree2", 10 | "robotbit.StpCarMove|block": "步進小車 前進 |距離(cm) %distance|輪直徑(mm) %diameter", 11 | "robotbit.StpCarTurn|block": "步進小車 轉向 |角度 %turn|輪直徑(mm) %diameter|輪距(mm) %track", 12 | "robotbit.MotorRun|block": "馬達|%index|速度 %speed", 13 | "robotbit.MotorRunDual|block": "馬達|%motor1|速度 %speed1|%motor2|速度 %speed2", 14 | "robotbit.MotorRunDelay|block": "馬達|%index|速度 %speed|延時 %delay|s", 15 | "robotbit.MotorStop|block": "馬達 停止|%index|", 16 | "robotbit.MotorStopAll|block": "停止所有馬達", 17 | "robotbit.RgbUltrasonic|block": "貓耳RGB超聲波|腳位 %pin", 18 | "robotbit.HoleUltrasonic|block": "貓耳洞超聲波|腳位 %pin|單位 %unit", 19 | "robotbit.MatrixDraw|block": "LED矩陣 描點|X %x|Y %y", 20 | "robotbit.MatrixClear|block": "清空 LED矩陣", 21 | 22 | "robotbit.ValueUnit.Millimeter|block": "毫米", 23 | "robotbit.ValueUnit.Centimeters|block": "厘米", 24 | 25 | "{id:group}Servo": "舵機", 26 | "{id:group}Motor": "馬達/步進", 27 | "{id:group}Modules": "外部模塊" 28 | } 29 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KittenBot/pxt-robotbit/ca5a3606c94b2e35b8d237fd96d1d6aae8093127/icon.png -------------------------------------------------------------------------------- /icon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KittenBot/pxt-robotbit/ca5a3606c94b2e35b8d237fd96d1d6aae8093127/icon2.png -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Riven 3 | modified from pxt-servo/servodriver.ts 4 | load dependency 5 | "robotbit": "file:../pxt-robotbit" 6 | */ 7 | 8 | 9 | //% color="#31C7D5" weight=10 icon="\uf1d0" 10 | namespace robotbit { 11 | const PCA9685_ADDRESS = 0x40 12 | const MODE1 = 0x00 13 | const MODE2 = 0x01 14 | const SUBADR1 = 0x02 15 | const SUBADR2 = 0x03 16 | const SUBADR3 = 0x04 17 | const PRESCALE = 0xFE 18 | const LED0_ON_L = 0x06 19 | const LED0_ON_H = 0x07 20 | const LED0_OFF_L = 0x08 21 | const LED0_OFF_H = 0x09 22 | const ALL_LED_ON_L = 0xFA 23 | const ALL_LED_ON_H = 0xFB 24 | const ALL_LED_OFF_L = 0xFC 25 | const ALL_LED_OFF_H = 0xFD 26 | 27 | const STP_CHA_L = 2047 28 | const STP_CHA_H = 4095 29 | 30 | const STP_CHB_L = 1 31 | const STP_CHB_H = 2047 32 | 33 | const STP_CHC_L = 1023 34 | const STP_CHC_H = 3071 35 | 36 | const STP_CHD_L = 3071 37 | const STP_CHD_H = 1023 38 | 39 | // HT16K33 commands 40 | const HT16K33_ADDRESS = 0x70 41 | const HT16K33_BLINK_CMD = 0x80 42 | const HT16K33_BLINK_DISPLAYON = 0x01 43 | const HT16K33_BLINK_OFF = 0 44 | const HT16K33_BLINK_2HZ = 1 45 | const HT16K33_BLINK_1HZ = 2 46 | const HT16K33_BLINK_HALFHZ = 3 47 | const HT16K33_CMD_BRIGHTNESS = 0xE0 48 | 49 | export enum Servos { 50 | S1 = 0x01, 51 | S2 = 0x02, 52 | S3 = 0x03, 53 | S4 = 0x04, 54 | S5 = 0x05, 55 | S6 = 0x06, 56 | S7 = 0x07, 57 | S8 = 0x08 58 | } 59 | 60 | export enum Motors { 61 | M1A = 0x1, 62 | M1B = 0x2, 63 | M2A = 0x3, 64 | M2B = 0x4 65 | } 66 | 67 | export enum Steppers { 68 | M1 = 0x1, 69 | M2 = 0x2 70 | } 71 | 72 | export enum SonarVersion { 73 | V1 = 0x1, 74 | V2 = 0x2 75 | } 76 | 77 | export enum Turns { 78 | //% blockId="T1B4" block="1/4" 79 | T1B4 = 90, 80 | //% blockId="T1B2" block="1/2" 81 | T1B2 = 180, 82 | //% blockId="T1B0" block="1" 83 | T1B0 = 360, 84 | //% blockId="T2B0" block="2" 85 | T2B0 = 720, 86 | //% blockId="T3B0" block="3" 87 | T3B0 = 1080, 88 | //% blockId="T4B0" block="4" 89 | T4B0 = 1440, 90 | //% blockId="T5B0" block="5" 91 | T5B0 = 1800 92 | } 93 | 94 | export enum ValueUnit { 95 | //% block="mm" 96 | Millimeter, 97 | //% block="cm" 98 | Centimeters 99 | } 100 | 101 | let initialized = false 102 | let initializedMatrix = false 103 | let neoStrip: neopixel.Strip; 104 | let matBuf = pins.createBuffer(17); 105 | let distanceBuf = 0; 106 | 107 | function i2cwrite(addr: number, reg: number, value: number) { 108 | let buf = pins.createBuffer(2) 109 | buf[0] = reg 110 | buf[1] = value 111 | pins.i2cWriteBuffer(addr, buf) 112 | } 113 | 114 | function i2ccmd(addr: number, value: number) { 115 | let buf = pins.createBuffer(1) 116 | buf[0] = value 117 | pins.i2cWriteBuffer(addr, buf) 118 | } 119 | 120 | function i2cread(addr: number, reg: number) { 121 | pins.i2cWriteNumber(addr, reg, NumberFormat.UInt8BE); 122 | let val = pins.i2cReadNumber(addr, NumberFormat.UInt8BE); 123 | return val; 124 | } 125 | 126 | function initPCA9685(): void { 127 | i2cwrite(PCA9685_ADDRESS, MODE1, 0x00) 128 | setFreq(50); 129 | for (let idx = 0; idx < 16; idx++) { 130 | setPwm(idx, 0, 0); 131 | } 132 | initialized = true 133 | } 134 | 135 | function setFreq(freq: number): void { 136 | // Constrain the frequency 137 | let prescaleval = 25000000; 138 | prescaleval /= 4096; 139 | prescaleval /= freq; 140 | prescaleval -= 1; 141 | let prescale = prescaleval; //Math.Floor(prescaleval + 0.5); 142 | let oldmode = i2cread(PCA9685_ADDRESS, MODE1); 143 | let newmode = (oldmode & 0x7F) | 0x10; // sleep 144 | i2cwrite(PCA9685_ADDRESS, MODE1, newmode); // go to sleep 145 | i2cwrite(PCA9685_ADDRESS, PRESCALE, prescale); // set the prescaler 146 | i2cwrite(PCA9685_ADDRESS, MODE1, oldmode); 147 | control.waitMicros(5000); 148 | i2cwrite(PCA9685_ADDRESS, MODE1, oldmode | 0xa1); 149 | } 150 | 151 | function setPwm(channel: number, on: number, off: number): void { 152 | if (channel < 0 || channel > 15) 153 | return; 154 | //serial.writeValue("ch", channel) 155 | //serial.writeValue("on", on) 156 | //serial.writeValue("off", off) 157 | 158 | let buf = pins.createBuffer(5); 159 | buf[0] = LED0_ON_L + 4 * channel; 160 | buf[1] = on & 0xff; 161 | buf[2] = (on >> 8) & 0xff; 162 | buf[3] = off & 0xff; 163 | buf[4] = (off >> 8) & 0xff; 164 | pins.i2cWriteBuffer(PCA9685_ADDRESS, buf); 165 | } 166 | 167 | 168 | function setStepper(index: number, dir: boolean): void { 169 | if (index == 1) { 170 | if (dir) { 171 | setPwm(0, STP_CHA_L, STP_CHA_H); 172 | setPwm(2, STP_CHB_L, STP_CHB_H); 173 | setPwm(1, STP_CHC_L, STP_CHC_H); 174 | setPwm(3, STP_CHD_L, STP_CHD_H); 175 | } else { 176 | setPwm(3, STP_CHA_L, STP_CHA_H); 177 | setPwm(1, STP_CHB_L, STP_CHB_H); 178 | setPwm(2, STP_CHC_L, STP_CHC_H); 179 | setPwm(0, STP_CHD_L, STP_CHD_H); 180 | } 181 | } else { 182 | if (dir) { 183 | setPwm(4, STP_CHA_L, STP_CHA_H); 184 | setPwm(6, STP_CHB_L, STP_CHB_H); 185 | setPwm(5, STP_CHC_L, STP_CHC_H); 186 | setPwm(7, STP_CHD_L, STP_CHD_H); 187 | } else { 188 | setPwm(7, STP_CHA_L, STP_CHA_H); 189 | setPwm(5, STP_CHB_L, STP_CHB_H); 190 | setPwm(6, STP_CHC_L, STP_CHC_H); 191 | setPwm(4, STP_CHD_L, STP_CHD_H); 192 | } 193 | } 194 | } 195 | 196 | function stopMotor(index: number) { 197 | setPwm((index - 1) * 2, 0, 0); 198 | setPwm((index - 1) * 2 + 1, 0, 0); 199 | } 200 | 201 | function matrixInit() { 202 | i2ccmd(HT16K33_ADDRESS, 0x21);// turn on oscillator 203 | i2ccmd(HT16K33_ADDRESS, HT16K33_BLINK_CMD | HT16K33_BLINK_DISPLAYON | (0 << 1)); 204 | i2ccmd(HT16K33_ADDRESS, HT16K33_CMD_BRIGHTNESS | 0xF); 205 | } 206 | 207 | function matrixShow() { 208 | matBuf[0] = 0x00; 209 | pins.i2cWriteBuffer(HT16K33_ADDRESS, matBuf); 210 | } 211 | 212 | 213 | /** 214 | * Init RGB pixels mounted on robotbit 215 | */ 216 | //% blockId="robotbit_rgb" block="RGB" 217 | //% weight=70 218 | export function rgb(): neopixel.Strip { 219 | if (!neoStrip) { 220 | neoStrip = neopixel.create(DigitalPin.P16, 4, NeoPixelMode.RGB) 221 | } 222 | 223 | return neoStrip; 224 | } 225 | 226 | /** 227 | * Servo Execute 228 | * @param index Servo Channel; eg: S1 229 | * @param degree [0-180] degree of servo; eg: 0, 90, 180 230 | */ 231 | //% blockId=robotbit_servo block="Servo|%index|degree %degree" 232 | //% group="Servo" weight=62 233 | //% degree.min=0 degree.max=180 234 | //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 235 | export function Servo(index: Servos, degree: number): void { 236 | if (!initialized) { 237 | initPCA9685() 238 | } 239 | // 50hz: 20,000 us 240 | let v_us = (degree * 1800 / 180 + 600) // 0.6 ~ 2.4 241 | let value = v_us * 4096 / 20000 242 | setPwm(index + 7, 0, value) 243 | } 244 | 245 | /** 246 | * Geek Servo 247 | * @param index Servo Channel; eg: S1 248 | * @param degree [-45-225] degree of servo; eg: -45, 90, 225 249 | */ 250 | //% blockId=robotbit_gservo block="Geek Servo|%index|degree %degree" 251 | ///% group="Servo" weight=61 252 | //% degree.min=-45 degree.max=225 253 | //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 254 | export function GeekServo(index: Servos, degree: number): void { 255 | if (!initialized) { 256 | initPCA9685() 257 | } 258 | // 50hz: 20,000 us 259 | let v_us = ((degree - 90) * 20 / 3 + 1500) // 0.6 ~ 2.4 260 | let value = v_us * 4096 / 20000 261 | setPwm(index + 7, 0, value) 262 | } 263 | 264 | /** 265 | * GeekServo2KG 266 | * @param index Servo Channel; eg: S1 267 | * @param degree [0-360] degree of servo; eg: 0, 180, 360 268 | */ 269 | //% blockId=robotbit_gservo2kg block="GeekServo2KG|%index|degree %degree" 270 | //% group="Servo" weight=60 271 | //% blockGap=50 272 | //% degree.min=0 degree.max=360 273 | //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 274 | export function GeekServo2KG(index: Servos, degree: number): void { 275 | if (!initialized) { 276 | initPCA9685() 277 | } 278 | // 50hz: 20,000 us 279 | //let v_us = (degree * 2000 / 360 + 500) 0.5 ~ 2.5 280 | let v_us = (Math.floor((degree) * 2000 / 350) + 500) //fixed 281 | let value = v_us * 4096 / 20000 282 | setPwm(index + 7, 0, value) 283 | } 284 | 285 | /** 286 | * GeekServo5KG 287 | * @param index Servo Channel; eg: S1 288 | * @param degree [0-360] degree of servo; eg: 0, 180, 360 289 | */ 290 | //% blockId=robotbit_gservo5kg block="GeekServo5KG|%index|degree %degree" 291 | //% group="Servo" weight=59 292 | //% degree.min=0 degree.max=360 293 | //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 294 | export function GeekServo5KG(index: Servos, degree: number): void { 295 | if (!initialized) { 296 | initPCA9685() 297 | } 298 | const minInput = 0; 299 | const maxInput = 355;//理论值为360 300 | const minOutput = 500; 301 | const maxOutput = 2500; 302 | const v_us = ((degree - minInput) / (maxInput - minInput)) * (maxOutput - minOutput) + minOutput; 303 | 304 | let value = v_us * 4096 / 20000 305 | setPwm(index + 7, 0, value) 306 | } 307 | 308 | //% blockId=robotbit_gservo5kg_motor block="GeekServo5KG_MotorEN|%index|speed %speed" 309 | //% group="Servo" weight=58 310 | //% speed.min=-255 speed.max=255 311 | //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 312 | export function GeekServo5KG_Motor(index: Servos, speed: number): void { //5KG的电机模式 3000-5000 4000是回中 313 | if (!initialized) { 314 | initPCA9685() 315 | } 316 | const minInput = -255; 317 | const maxInput = 255; 318 | const minOutput = 5000; 319 | const maxOutput = 3000; 320 | 321 | const v_us = ((speed - minInput) / (maxInput - minInput)) * (maxOutput - minOutput) + minOutput; 322 | let value = v_us * 4096 / 20000 323 | setPwm(index + 7, 0, value) 324 | } 325 | 326 | 327 | //% blockId=robotbit_stepper_degree block="Stepper 28BYJ-48|%index|degree %degree" 328 | //% group="Motor" weight=54 329 | export function StepperDegree(index: Steppers, degree: number): void { 330 | if (!initialized) { 331 | initPCA9685() 332 | } 333 | setStepper(index, degree > 0); 334 | degree = Math.abs(degree); 335 | basic.pause(10240 * degree / 360); 336 | MotorStopAll() 337 | } 338 | 339 | 340 | //% blockId=robotbit_stepper_turn block="Stepper 28BYJ-48|%index|turn %turn" 341 | //% group="Motor" weight=53 342 | export function StepperTurn(index: Steppers, turn: Turns): void { 343 | let degree = turn; 344 | StepperDegree(index, degree); 345 | } 346 | 347 | //% blockId=robotbit_stepper_dual block="Dual Stepper(Degree) |M1 %degree1| M2 %degree2" 348 | //% group="Motor" weight=52 349 | export function StepperDual(degree1: number, degree2: number): void { 350 | if (!initialized) { 351 | initPCA9685() 352 | } 353 | setStepper(1, degree1 > 0); 354 | setStepper(2, degree2 > 0); 355 | degree1 = Math.abs(degree1); 356 | degree2 = Math.abs(degree2); 357 | basic.pause(10240 * Math.min(degree1, degree2) / 360); 358 | if (degree1 > degree2) { 359 | stopMotor(3); stopMotor(4); 360 | basic.pause(10240 * (degree1 - degree2) / 360); 361 | } else { 362 | stopMotor(1); stopMotor(2); 363 | basic.pause(10240 * (degree2 - degree1) / 360); 364 | } 365 | 366 | MotorStopAll() 367 | } 368 | 369 | /** 370 | * Stepper Car move forward 371 | * @param distance Distance to move in cm; eg: 10, 20 372 | * @param diameter diameter of wheel in mm; eg: 48 373 | */ 374 | //% blockId=robotbit_stpcar_move block="Car Forward|Distance(cm) %distance|Wheel Diameter(mm) %diameter" 375 | //% group="Motor" weight=51 376 | export function StpCarMove(distance: number, diameter: number): void { 377 | if (!initialized) { 378 | initPCA9685() 379 | } 380 | let delay = 10240 * 10 * distance / 3 / diameter; // use 3 instead of pi 381 | setStepper(1, delay > 0); 382 | setStepper(2, delay > 0); 383 | delay = Math.abs(delay); 384 | basic.pause(delay); 385 | MotorStopAll() 386 | } 387 | 388 | /** 389 | * Stepper Car turn by degree 390 | * @param turn Degree to turn; eg: 90, 180, 360 391 | * @param diameter diameter of wheel in mm; eg: 48 392 | * @param track track width of car; eg: 125 393 | */ 394 | //% blockId=robotbit_stpcar_turn block="Car Turn|Degree %turn|Wheel Diameter(mm) %diameter|Track(mm) %track" 395 | //% group="Motor" weight=50 396 | //% blockGap=50 397 | export function StpCarTurn(turn: number, diameter: number, track: number): void { 398 | if (!initialized) { 399 | initPCA9685() 400 | } 401 | let delay = 10240 * turn * track / 360 / diameter; 402 | setStepper(1, delay < 0); 403 | setStepper(2, delay > 0); 404 | delay = Math.abs(delay); 405 | basic.pause(delay); 406 | MotorStopAll() 407 | } 408 | 409 | //% blockId=robotbit_motor_run block="Motor|%index|speed %speed" 410 | //% group="Motor" weight=59 411 | //% speed.min=-255 speed.max=255 412 | //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 413 | export function MotorRun(index: Motors, speed: number): void { 414 | if (!initialized) { 415 | initPCA9685() 416 | } 417 | speed = speed * 16; // map 255 to 4096 418 | if (speed >= 4096) { 419 | speed = 4095 420 | } 421 | if (speed <= -4096) { 422 | speed = -4095 423 | } 424 | if (index > 4 || index <= 0) 425 | return 426 | let pp = (index - 1) * 2 427 | let pn = (index - 1) * 2 + 1 428 | if (speed >= 0) { 429 | setPwm(pp, 0, speed) 430 | setPwm(pn, 0, 0) 431 | } else { 432 | setPwm(pp, 0, 0) 433 | setPwm(pn, 0, -speed) 434 | } 435 | } 436 | 437 | 438 | /** 439 | * Execute two motors at the same time 440 | * @param motor1 First Motor; eg: M1A, M1B 441 | * @param speed1 [-255-255] speed of motor; eg: 150, -150 442 | * @param motor2 Second Motor; eg: M2A, M2B 443 | * @param speed2 [-255-255] speed of motor; eg: 150, -150 444 | */ 445 | //% blockId=robotbit_motor_dual block="Motor|%motor1|speed %speed1|%motor2|speed %speed2" 446 | //% group="Motor" weight=58 447 | //% speed1.min=-255 speed1.max=255 448 | //% speed2.min=-255 speed2.max=255 449 | //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 450 | export function MotorRunDual(motor1: Motors, speed1: number, motor2: Motors, speed2: number): void { 451 | MotorRun(motor1, speed1); 452 | MotorRun(motor2, speed2); 453 | } 454 | 455 | /** 456 | * Execute single motors with delay 457 | * @param index Motor Index; eg: M1A, M1B, M2A, M2B 458 | * @param speed [-255-255] speed of motor; eg: 150, -150 459 | * @param delay seconde delay to stop; eg: 1 460 | */ 461 | //% blockId=robotbit_motor_rundelay block="Motor|%index|speed %speed|delay %delay|s" 462 | //% group="Motor" weight=57 463 | //% speed.min=-255 speed.max=255 464 | //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 465 | export function MotorRunDelay(index: Motors, speed: number, delay: number): void { 466 | MotorRun(index, speed); 467 | basic.pause(delay * 1000); 468 | MotorRun(index, 0); 469 | } 470 | 471 | 472 | 473 | //% blockId=robotbit_stop block="Motor Stop|%index|" 474 | //% group="Motor" weight=56 475 | export function MotorStop(index: Motors): void { 476 | MotorRun(index, 0); 477 | } 478 | 479 | //% blockId=robotbit_stop_all block="Motor Stop All" 480 | //% group="Motor" weight=55 481 | //% blockGap=50 482 | export function MotorStopAll(): void { 483 | if (!initialized) { 484 | initPCA9685() 485 | } 486 | for (let idx = 1; idx <= 4; idx++) { 487 | stopMotor(idx); 488 | } 489 | } 490 | 491 | //% blockId=robotbit_matrix_draw block="Matrix Draw|X %x|Y %y" 492 | //% group="Modules" weight=44 493 | export function MatrixDraw(x: number, y: number): void { 494 | if (!initializedMatrix) { 495 | matrixInit(); 496 | initializedMatrix = true; 497 | } 498 | x = Math.round(x) 499 | y = Math.round(y) 500 | 501 | let idx = y * 2 + Math.idiv(x, 8); 502 | 503 | let tmp = matBuf[idx + 1]; 504 | tmp |= (1 << (x % 8)); 505 | matBuf[idx + 1] = tmp; 506 | } 507 | 508 | //% blockId=robotbit_matrix_refresh block="Matrix Refresh" 509 | //% group="Modules" weight=43 510 | export function MatrixRefresh(): void { 511 | if (!initializedMatrix) { 512 | matrixInit(); 513 | initializedMatrix = true; 514 | } 515 | matrixShow(); 516 | } 517 | 518 | /* 519 | //% blockId=robotbit_matrix_clean block="Matrix Clean|X %x|Y %y" 520 | //% group="Servo" weight=68 521 | export function MatrixClean(x: number, y: number): void { 522 | if (!initializedMatrix) { 523 | matrixInit(); 524 | initializedMatrix = true; 525 | } 526 | let idx = y * 2 + x / 8; 527 | // todo: bitwise not throw err 528 | matBuf[idx + 1] &=~(1 << (x % 8)); 529 | matrixShow(); 530 | } 531 | */ 532 | 533 | //% blockId=robotbit_matrix_clear block="Matrix Clear" 534 | //% group="Modules" weight=42 535 | //% blockGap=50 536 | export function MatrixClear(): void { 537 | if (!initializedMatrix) { 538 | matrixInit(); 539 | initializedMatrix = true; 540 | } 541 | for (let i = 0; i < 16; i++) { 542 | matBuf[i + 1] = 0; 543 | } 544 | matrixShow(); 545 | } 546 | 547 | /** 548 | * signal pin 549 | * @param pin singal pin; eg: DigitalPin.P1 550 | */ 551 | //% blockId=robotbit_rgbultrasonic block="Ultrasonic(with RGB)|pin %pin" 552 | //% group="Modules" weight=41 553 | export function RgbUltrasonic(pin: DigitalPin): number { 554 | pins.setPull(pin, PinPullMode.PullNone); 555 | pins.digitalWritePin(pin, 0); 556 | control.waitMicros(2); 557 | pins.digitalWritePin(pin, 1); 558 | control.waitMicros(10); 559 | pins.digitalWritePin(pin, 0); 560 | 561 | // read pulse 562 | let d = pins.pulseIn(pin, PulseValue.High, 25000); 563 | let ret = d; 564 | // filter timeout spikes 565 | if (ret == 0 && distanceBuf != 0) { 566 | ret = distanceBuf; 567 | } 568 | distanceBuf = d; 569 | 570 | return Math.floor(ret * 9 / 6 / 58); 571 | } 572 | 573 | /** 574 | * signal pin 575 | * @param pin singal pin; eg: DigitalPin.P1 576 | * @param unit desired conversion unit 577 | */ 578 | //% blockId=robotbit_holeultrasonicver block="Ultrasonic(with LEGO Hole)|pin %pin|unit %unit" 579 | //% group="Modules" weight=40 580 | export function HoleUltrasonic(pin: DigitalPin, unit: ValueUnit): number { 581 | pins.setPull(pin, PinPullMode.PullNone); 582 | // pins.setPull(pin, PinPullMode.PullDown); 583 | pins.digitalWritePin(pin, 0); 584 | control.waitMicros(2); 585 | pins.digitalWritePin(pin, 1); 586 | control.waitMicros(10); 587 | pins.digitalWritePin(pin, 0); 588 | pins.setPull(pin, PinPullMode.PullUp); 589 | 590 | // read pulse 591 | let d = pins.pulseIn(pin, PulseValue.High, 30000); 592 | let ret = d; 593 | // filter timeout spikes 594 | if (ret == 0 && distanceBuf != 0) { 595 | ret = distanceBuf; 596 | } 597 | distanceBuf = d; 598 | pins.digitalWritePin(pin, 0); 599 | basic.pause(15) 600 | if (parseInt(control.hardwareVersion()) == 2) { 601 | d = ret *10 /58; 602 | } 603 | else{ 604 | // return Math.floor(ret / 40 + (ret / 800)); 605 | d = ret * 15 / 58; 606 | } 607 | switch (unit) { 608 | case ValueUnit.Millimeter: return Math.floor(d) 609 | case ValueUnit.Centimeters: return Math.floor(d/10) 610 | default: return d; 611 | } 612 | } 613 | } 614 | -------------------------------------------------------------------------------- /pxt.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "robotbit", 3 | "version": "0.4.6", 4 | "description": "Extension for Kittenbot Robotbit", 5 | "license": "MIT", 6 | "dependencies": { 7 | "core": "*", 8 | "neopixel": "github:kittenbot/pxt-neopixel", 9 | "SensorPlus": "github:kittenbothk/pxt-moduleplus", 10 | "microphone": "*" 11 | }, 12 | "files": [ 13 | "README.md", 14 | "main.ts", 15 | "_locales/zh-cn/robotbit-strings.json", 16 | "_locales/zh-tw/robotbit-strings.json" 17 | ], 18 | "testFiles": [ 19 | "test.ts" 20 | ], 21 | "public": true, 22 | "targetVersions": { 23 | "target": "6.0.18", 24 | "targetId": "microbit" 25 | }, 26 | "supportedTargets": [ 27 | "microbit" 28 | ], 29 | "preferredEditor": "tsprj" 30 | } 31 | -------------------------------------------------------------------------------- /test.ts: -------------------------------------------------------------------------------- 1 | // tests go here; this will not be compiled when this package is used as a library 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "noImplicitAny": true, 5 | "outDir": "built", 6 | "rootDir": "." 7 | }, 8 | "include": [ 9 | "../../**/*" 10 | ], 11 | "exclude": ["pxt_modules/**/*test.ts"] 12 | } 13 | --------------------------------------------------------------------------------